Home > Net >  Confusion about select()
Confusion about select()

Time:02-16

I'm busy with this for 2 days now and still don't understand it. What does select() do in this code?

I know that if there is an incoming connection that can be accepted, the copy.fd_array[] will contain ListenSocket but when the while loop repeats it's still there. So how do we know if a client is disconnected? What does fd_set copy contain after the select() call?

        fd_set master;
        FD_ZERO(&master);
        FD_SET(ListenSocket, &master);

        while (1)
        {
            fd_set copy = master;

            select(FD_SETSIZE, &copy, NULL, NULL, NULL);

            for (int i = 0; i < FD_SETSIZE; i  )
            {    
                // If new connection
                if (FD_ISSET(ListenSocket, &copy)) 
                {
                    printf("[ ] New connection\n");
                    // Accept connection
                    SOCKET AcceptedClient = accept(ListenSocket, NULL, NULL);
                    FD_SET(AcceptedClient, &master);

                    // Send welcome message to client
                    char buff[128] = "Hello Client!";
                    send(AcceptedClient, buff, sizeof(buff), 0);
                }
            }
        }

CodePudding user response:

I'm busy with this for 2 days now and still don't understand it.

It's no wonder that you don't understand the code: The code in the example is nonsense.

Checking the ListenSocket should be done outside the for loop. And FD_ISSET must also be checked for the connections accepted using accept.

The correct code inside the while loop would look like this:

fd_set copy = master;

select(FD_SETSIZE, &copy, NULL, NULL, NULL);

// If new connection
if (FD_ISSET(ListenSocket, &copy)) 
{
    ...
}

for (int i = 0; i < FD_SETSIZE; i  )
{    
    // If an existing connection has data
    // or the connection has been closed
    if ((i != ListenSocket) && FD_ISSET(i, &copy)) 
    {
        nBytes = recv(i, buffer, maxBytes, 0);
        // Connection dropped
        if(nBytes < 1)
        {
            close(i); // other OSs (Linux, MacOS ...)
            // closesocket(i); // Windows
            FD_CLR(i, &master);
        }
        // Data received
        else
        {
            ...
        }
    }
}

I know that if there is an incoming connection that can be accepted, the copy.fd_array[] will contain ListenSocket but when the while loop repeats it's still there.
What does fd_set copy contain after the select() call?

First of all: Before calling select(), copy.fd_array[] must contain all socket handles that you are interested in. This means it must contain ListenSocket and all handles returned by accept().

master.fd_array[] contains all these handles, so fd_set copy = master; will ensure that copy.fd_array[] also contains all these handles.

select() (with NULL as last argument) will wait until at least one socket becomes "available". This means that it will wait until at least one of the following conditions is true:

  • A connection accepted using accept() is closed by the other side
  • a connection accepted using accept() has data that can be received
  • there is a new connection that can be accepted using accept(ListenSocket...)

As soon as one condition is fulfilled, select() removes all other handles from copy.fd_array[]:

  • ListenSocket is removed from copy.fd_array[] if there is no incoming connection
  • A handle returned by accept() is removed from that array if the connection has neither been closed nor new data is available

If two events happen the same time, copy.fd_array[] will contain more than one handle.

You use FD_ISSET() to check if some handle is still in the array.

So how do we know if a client is disconnected?

When you detect FD_ISSET(i, &copy) for a value i that has been returned by accept(), you must call recv() (under Linux read() would also work):

If recv() returns 0 (or negative in the case of errors), the other computer has dropped the connection. You must call close() (closesocket() on Windows) and remove the handle from copy.fd_array[] (this means: you must remove it from master.fd_array[] because of the line fd_set copy = master;).

If recv() returns a positive value, this is the number of bytes that have been received.

  • Related