Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

select() always returns 1; TCP connected socket troubles in c++

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.

like image 244
Ryan Avatar asked Jan 21 '23 16:01

Ryan


2 Answers

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.

like image 77
Greg Hewgill Avatar answered Jan 28 '23 11:01

Greg Hewgill


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);
like image 27
harper Avatar answered Jan 28 '23 10:01

harper