sleighty
sleighty

Reputation: 1075

How to handle blocking read() call when reading from a socket?

I'm writing a simple IRC client program in C for self-teaching purposes, and am having trouble understanding the behavior of the read() function when called reading from a socket file descriptor.

The following code snippet works and prints the same output as running

$ echo "NICK gertrudes\r\nUSER a 0 * d\r\n" | nc chat.freenode.net 6667

in the terminal, which is the same as my program prints so far:

while ((n = read(sockfd, buffer, sizeof(buffer)-1)) > 0) {
    printf("\nloop\n");
    buffer[n] = '\0';
    if (fputs(buffer, stdout) == EOF)
        error("fputs");
}
if (n < 0)
    error("reading from socket");

printf("out of the loop\n");

What I fail to understand is why the program never gets to the final printf call, and rather sits there as if waiting for more from the server. Does that mean that the last reply was longer than 0 and the IRC server just won't send anything new until I send another command?

If so (at the risk of going off-topic here), and read() is blocking, where would I write the logic of sending commands to the server while the program is waiting for that call to return?

Upvotes: 2

Views: 4330

Answers (2)

Luis Colorado
Luis Colorado

Reputation: 12668

Despite your program not being complete, there are several things that you are wrongly assuming. Let's comment these in your code.

while ((n = read(sockfd, buffer, sizeof(buffer)-1)) > 0) {
  • It's good to read sizeof(buffer)-1 if you plan to complete it with a \0 byte, but think that you can receive a \0 from the socket, if you want to be general, don't assume you are always reading text. Many security exploits come from errors like this. The programmer assumes (erroneously) that the data is ascii text, and someone exploits a buffer overrun (this is not the case) or something illegal, feeding a lot of null characters to make it fail.

    printf("\nloop\n");
    buffer[n] = '\0';
    if (fputs(buffer, stdout) == EOF)
    
  • This is a VERY common mistake... you are used to see that when you put a \n at the end of a buffer, stdio prints everything until the last buffer as soon as it sees it. Well, for this to happen, stdio checks if the descriptor is associated to a terminal (by means of an ioctl(2) call, or a call to isatty(3)). This is no longer true with sockets, so probably your buffer has been copied to stdio buffer, and stdio is waiting for the buffer to fill or you to explicitly flush the buffer with fflush(3) before calling write(2) to send all the data over it.

        error("fputs");
    
  • Do a fflush(stdout); at this point, so you are sure all your data is sent to the peer, before continuing, or don't use stdio at all (use simple write(2) calls, until you are proficient enough to prepare a thread that select(2)s on the socket to feed more data as soon as it is ready to accept more data)

    }
    if (n < 0)
        error("reading from socket");
    
    printf("out of the loop\n");
    

Upvotes: 1

user207421
user207421

Reputation: 310915

What I fail to understand is why the program never gets to the final printf call, and rather sits there as if waiting for more from the server.

It is waitng for more from the server. read() will return zero when the peer disconnects, and not before.

Upvotes: 2

Related Questions