C- Unix Sockets - Non-blocking read

31,299

Solution 1

You can make socket not blockable, as suggested in another post plus use select to wait input with timeout, like this:

fd_set         input;
FD_ZERO(&input);
FD_SET(sd, &input);
struct timeval timeout;
timeout.tv_sec  = sec;
timeout.tv_usec = msec * 1000;
int n = select(sd + 1, &input, NULL, NULL, &timeout);
if (n == -1) {
    //something wrong
} else if (n == 0)
    continue;//timeout
if (!FD_ISSET(sd, &input))
   ;//again something wrong
//here we can call not blockable read

Solution 2

fcntl(socket, F_SETFL, O_NONBLOCK);

or, if you have other flags:

int x;
x=fcntl(socket ,F_GETFL, 0);
fcntl(socket, F_SETFL, x | O_NONBLOCK);

then check the return value of read to see whether there was data available.

note: a bit of googling will yield you lots of full examples.

You can also use blocking sockets, and "peek" with select with a timeout. It seems more appropriate here so you don't do busy wait.

Solution 3

The best thing is likely to get rid of the extra thread and use select() or poll() to handle everything in one thread.

If you want to keep the thread, one thing you can do is call shutdown() on the socket with SHUT_RDWR, which will shut down the connection, wake up all threads blocked on it but keep the file descriptor valid. After you have joined the reader thread, you can then close the socket. Note that this only works on sockets, not on other types of file descriptor.

Solution 4

Look for function setsockopt with option SO_RCVTIMEO.

Share:
31,299
will
Author by

will

Updated on November 13, 2020

Comments

  • will
    will over 3 years

    I am trying to make a simple client-server chat program. On the client side I spin off another thread to read any incomming data from the server. The problem is, I want to gracefully terminate that second thread when a person logs out from the main thread. I was trying to use a shared variable 'running' to terminate, problem is, the socket read() command is a blocking command, so if I do while(running == 1), the server has to send something before the read returns and the while condition can be checked again. I am looking for a method (with common unix sockets only) to do a non-blocking read, basically some form of peek() would work, for I can continually check the loop to see if I'm done.

    The reading thread loop is below, right now it does not have any mutex's for the shared variables, but I plan to add that later don't worry! ;)

    void *serverlisten(void *vargp)
    {
        while(running == 1)
        {
            read(socket, readbuffer, sizeof(readbuffer));
            printf("CLIENT RECIEVED: %s\n", readbuffer);
        }
        pthread_exit(NULL);
    }
    
  • will
    will over 12 years
    what does the sd+1 mean in this context? I ran it like this, and it worked, but it would loop through the loop like 20ish times, and then stop, till new input was created, however it did terminate gracefully like I wanted, the only concern is the 20 times. its almost like it was running during the timeout or something
  • fghj
    fghj over 12 years
    You can type "man select" in your terminal to get detailed description of select function. As the first argument selct take the biggest by value descriptor plus 1. Because of at here we have only one descriptor, I just write (sd + 1).
  • fghj
    fghj over 12 years
    Not understand note about 20 times. My code in context of your first question should works like this: 1)check flag(time to exit?) 2)sleep untill input or timeout 3)read something 4)goto (1). So it exit with some delay. But you should handle situation when other side close connection, then select return control to you, but "read" will read 0 bytes, and you get busy loop. So you should check result of "read" and exit loop if it zero.
  • will
    will over 12 years
    I figured it out, thanks, it was that I was printing inside the loop when there was no input, it was a flushing issue all solved, thankyou for helping :)