0

This is not a homework problem, I promise.

I'm writing a time series database implementation as a way to learn C.

I have a client/server pair that I've written. The server is currently an echo server listening to a socket on a port. The client connects to that port and sends lines of input to it. It uses readline to get input, sends it to the client socket, recvs a line from the client socket, and prints the line to the terminal. Rinse, repeat. The client returns when it gets an EOF from recv, at which point it knows the connection is closed.

The problem is that readline is blocking, such that if the server process is killed, i.e. I SIGINT it, the client is still blocking on readline. It isn't until after it sends, then recvs an EOF, that it will know the server is gone.

What I want to happen is for the client to get signaled if there's an EOF on recv and immediately exit.

What I think I need to do is to create 3 pthreads - 2 for a network client (send and recv) and 1 for the terminal. The terminal calls readline and blocks. It accepts input, and then uses a pthread_cond_t to signal the waiting network client send thread to send. The network client recv thread is constantly recving, which will block. If it is an EOF, it raises SIGINT, the handler to which pthread_kills all 3 threads, fprintfs something like "Connection closed by server.", and calls exit (yes, I know exit will terminate all threads anyways - it's an exercise in cleanliness and to see if I understand C).

Is this the appropriate approach? Obviously network client terminals do this all the time. What's the right approach?

jennykwan
  • 2,631
  • 1
  • 22
  • 33
  • 2
    There is no one correct approach. The classic reference is Rich Stevens' [_UNIX Network Programming_](http://www.amazon.com/Unix-Network-Programming-Volume-Networking/dp/0131411551) – arayq2 Jun 07 '14 at 05:29
  • "*It uses readline to get input ...*" "*it*" is who, the client or the server? – alk Jun 07 '14 at 09:53
  • And: One does not send "*to a socket*" but "*via*" a socket "*to*" some destination (here the client the server), as mostly the socket is used bi-directional. – alk Jun 07 '14 at 09:55

2 Answers2

1

If I'm understanding you correctly you would like to exit during a readline, which is before your write()/send(), when the server dies. Your approach is fine. The only way you will catch if the server has died is when you try to write()/send() you'll get a SIGPIPE which will cause an exit right after it. Because by default, no packets are sent on the connection unless there is data to send or acknowledge. So, if you are simply waiting for data from the peer, there is no way to tell if the peer has silently gone away, or just isn't ready to send/receive any more data yet. So you would need some poller for the SIGPIPE in a separate thread and exit when you get it. You could use this readline function to help you get started with the checking.

/**
 * Simple utility function that reads a line from a file descriptor fd, 
 * up to maxlen bytes -- ripped from Unix Network Programming, Stevens.
 */
int
readline(int fd, char *buf, int maxlen)
{
    int n, rc;
    char c;

    for (n = 1; n < maxlen; n++) {
    if ((rc = read(fd, &c, 1)) == 1) {
        *buf++ = c;
        if (c == '\n')
        break;
    } else if (rc == 0) {
        if (n == 1)
        return 0; // EOF, no data read
        else
        break; // EOF, read some data
    } else
        return -1; // error
    }

    *buf = '\0'; // null-terminate
    return n;
}
G--
  • 924
  • 7
  • 8
  • That makes sense. FWIW, a coworker said that calling `read` for each char is highly inefficient, because each call has to be trapped by the kernel. I don't know if that's true, and if so, what impact that has. In any case, I'm actually using `libreadline` to get the built in ctrl character functionality. I'll check out Stevens. – jennykwan Jun 07 '14 at 08:15
1

This question has been answered before. I got Stevens and chapter 5 suggested interleaving the readline call with a socket read to look for EOF. I did a Google search for libreadline and select/poll and saw this:

Interrupting c/c++ readline with signals

There's a way to use interleave I/O so libreadline doesn't just block:

http://www.delorie.com/gnu/docs/readline/rlman_41.html

Thanks @arayq2 and @G-- for suggesting Stevens!

Community
  • 1
  • 1
jennykwan
  • 2,631
  • 1
  • 22
  • 33