Sockets – C: Using Select to monitor two sockets

cselectserversockets

I am writing a server that connects to multiple clients. At the moment, I am using accept on a SOCK_STREAM socket to connect new clients. I want to be able to accept queries from clients that send a data gram to the server's SOCK_DGRAM socket. As accept just hangs until a client sends a request to connect to that socket, I need a way to monitor both sockets until one receives a request from a client and then to be able to deal with that request and return to monitoring the sockets. I do not need the requests to be handled immediately (i.e. if a client sends a query after another client requests to connect the querying client will have to wait for the server to finish the connecting client's request and vice-versa). I know that socket can alert the server as to how many sockets are ready to read/write but how can it tell the server which socket is ready? I.e. which socket is ready to read and which is ready to write? Here is my attempt where I know listener always needs a write after connecting and udpSock always needs a read.

 while(1){
        retval = select(max,&readSockSet, &writeSockSet, NULL, &timeout);
        if(retval){
            if(FD_ISSET(listener, &writeSockSet))
                printf("Client requesting to connect...\n");
            if(FD_ISSET(udpSock, &readSockSet))
                printf("Client query incoming...\n");
        }
    }

Best Answer

Rather than blocking on accept(), you use select() to tell you when a client is pending so you can then call accept() without blocking. Then you can monitor the TCP and UDP sockets at the same time. The code you have is already on the right track for exactly that task, however you are using the writing fdset to detect when to call accept() when you should be using the reading fdset instead, and you are not resetting the fdsets or timeout on each loop iteration as select() modifies them.

Try this:

fd_set readSockSet;
timeval timeout;
int smax = max(listener, udpSock);

while (1)
{
    FD_ZERO(&readSockSet);
    FD_SET(listener, &readSockSet);
    FD_SET(udpSock, &readSockSet);
    // add connected TCP clients, if needed...

    timeout.tv_sec = ...;
    timeout.tv_usec = ...;

    retval = select(smax+1, &readSockSet, NULL, NULL, &timeout);
    if (retval > 0)
    {
        if (FD_ISSET(listener, &readSockSet))
        {
            printf("TCP Client requesting to connect...\n");
            // call accept() and do something with the client socket ...
        }

        if (FD_ISSET(udpSock, &readSockSet))
        {
            printf("UDP Client query incoming...\n");
            // call recv()/read() to read the datagram ...
        }

        // check connected TCP clients, if needed...
    }
    else if (retval < 0)
    {
        // check errno/WSAGetLastError(), call perror(), etc ...
    }
}