4
votes

I'm doing a c++ project that requires a server to create a new thread to handle connections each time accept() returns a new socket descriptor. I am using select to decide when a connection attempt has taken place as well as when a client has sent data over the newly created client socket (the one that accept creates). So two functions and two selects - one for polling the socket dedicated to listening for connections, one for polling the socket created when a new connection is successful.

The behavior of the first case is what I expect - FD_ISSET returns true for the id of my listening socket only when a connection is requested, and is false until the next connection attempt. The second case does not work, even though the code is exactly the same with different fd_set and socket objects. I'm wondering if this stems from the TCP socket? Do these sockets always return true when polled by a select due to their streamy nature?

//working snippet

struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 500000;
fd_set readfds;
FD_ZERO(&readfds);
FD_SET(sid,&readfds);

//start server loop
for(;;){

    //check if listening socket has any client requrests, timeout at 500 ms
    int numsockets = select(sid+1,&readfds,NULL,NULL,&tv);
    if(numsockets == -1){
        if(errno == 4){
            printf("SIGINT recieved in select\n");
            FD_ZERO(&readfds);
            myhandler(SIGINT);
        }else{
            perror("server select");
            exit(1);
        }
    }

    //check if listening socket is ready to be read after select returns
    if(FD_ISSET(sid, &readfds)){
        int newsocketfd = accept(sid, (struct sockaddr*)&client_addr, &addrsize);
        if(newsocketfd == -1){
            if(errno == 4){
                printf("SIGINT recieved in accept\n");
                myhandler(SIGINT);
            }else{
                perror("server accept");
                exit(1);
            }
        }else{
            s->forkThreadForClient(newsocketfd);
        }
    } 






//non working snippet

//setup clients socket with select functionality
struct timeval ctv;
ctv.tv_sec = 0;
ctv.tv_usec = 500000;
fd_set creadfds;
FD_ZERO(&creadfds);
FD_SET(csid,&creadfds);



for(;;){

    //check if listening socket has any client requrests, timeout at 500 ms
    int numsockets = select(csid+1,&creadfds,NULL,NULL,&ctv);
    if(numsockets == -1){
        if(errno == 4){
            printf("SIGINT recieved in client select\n");
            FD_ZERO(&creadfds);
            myhandler(SIGINT);
        }else{
            perror("server select");
            exit(1);
        }
    }else{
        printf("Select returned %i\n",numsockets);
    }

    if(FD_ISSET(csid,&creadfds)){


        //read header
        unsigned char header[11];
        for(int i=0;i<11;i++){
            if(recv(csid, rubyte, 1, 0) != 0){
                printf("Received %X from client\n",*rubyte);
                header[i] = *rubyte;
            }
        }

Any help would be appreciated.


Thanks for the responses, but I don't believe it has much todo with the timeout value being inside the loop. I tested it and even with tv being reset and the fd_set being zeroed every time the server loops, select still returns 1 immediately. I feel like there's a problem with how select is treating my TCP socket. Any time I set selects highest socket id to encompass my TCP socket, it returns immediately with that socket set. Also, client does not send anything, just connects.

2
Could you fix your indenting so it looks right? It's really hard to follow with all the code smushed up against the left margin like that. - Greg Hewgill
You say it doesn't work. But what does it do instead ? - BatchyX
I can attest that select() works fine for TCP sockets, so there must be a bug in your code. From what you have pasted, perhaps csid is not set to the value that you think it is? - caf
By the way, thread creation is not particularly cheap so we typically use a pool for work like this rather than spawning a new thread for every single connection on demand - Lightness Races in Orbit
You are ignoring errors when receiving, and you are also ignoring end of stream recv() returns zero. You need to close the socket in both cases. This is why select() keeps returning 1: it is telling you to read the socket because there is an EOS pending, which you are ignoring, so it tells you again. - user207421

2 Answers

6
votes

One thing you must do is reset the value of tv to your desired timeout every time before you call select(). The select() function changes the values in tv to indicate how much time is left in the timeout, after returning from the function. If you fail to do this, your select() calls will end up using a timeout of zero, which is not efficient.

Some other operating systems implement select() differently, in such a way that they don't change the value of tv. Linux does change it, so you must reset it.

3
votes

Move

 FD_ZERO(&creadfds); 
 FD_SET(csid,&creadfds); 

into the loop. The function select() reports the result in this structure. You already retrieve the result with

FD_ISSET(csid,&creadfds);