8. Common Questions

Q: Where can I get those header files?
Q: What do I do when bind() reports "Address already in use"?
Q: How do I get a list of open sockets on the system?
Q: How can I tell if the remote side has closed connection?
Q: How do I build for Windows?
Q: How do I build for Solaris/SunOS? I keep getting linker errors when I try to compile!
Q: Why does select() keep falling out on a signal?
Q: How can I implement a timeout on a call to recv()?

Q: Where can I get those header files?

A: If you don't have them on your system already, you probably don't need them. Check the manual for your particular platform. If you're building for Windows, you only need to #include <winsock.h>.

Q: What do I do when bind() reports "Address already in use"?

A: You have to use setsockopt() with the SO_REUSEADDR option on the listening socket. Check out the section on bind() and the section on select() for an example.

Q: How do I get a list of open sockets on the system?

A: Use the netstat. Check the man page for full details, but you should get some good output just typing:


$ netstat

The only trick is determining which socket is associated with which program. :-)

Q: How can I tell if the remote side has closed connection?

A: You can tell because recv() will return 0.

Q: How do I build for Windows?

A: First, delete Windows and install Linux or BSD. };-). No, actually, just see the section on building for Windows in the introduction.

Q: How do I build for Solaris/SunOS? I keep getting linker errors when I try to compile!

A: The linker errors happen because Sun boxes don't automatically compile in the socket libraries. See the section on building for Solaris/SunOS in the introduction for an example of how to do this.

Q: Why does select() keep falling out on a signal?

A: Signals tend to cause blocked system calls to return -1 with errno set to EINTR. When you set up a signal handler with sigaction(), you can set the flag SA_RESTART, which is supposed to restart the system call after it was interrupted.

Naturally, this doesn't always work.

My favorite solution to this involves a goto statement. You know this irritates your professors to no end, so go for it!


select_restart:
    if ((err = select(fdmax+1, &readfds, NULL, NULL, NULL)) == -1) {
        if (errno == EINTR) {
            // some signal just interrupted us, so restart
            goto select_restart;
        }
        // handle the real error here:
        perror("select");
    } 

Sure, you don't need to use goto in this case; you can use other structures to control it. But I think the goto statement is actually cleaner.

Q: How can I implement a timeout on a call to recv()?

A: Use select()! It allows you to specify a timeout parameter for socket descriptors that you're looking to read from. Or, you could wrap the entire functionality in a single function, like this:


#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>

int recvtimeout(int s, char *buf, int len, int timeout)
{
    fd_set fds;
    int n;
    struct timeval tv;

    // set up the file descriptor set
    FD_ZERO(&fds);
    FD_SET(s, &fds);

    // set up the struct timeval for the timeout
    tv.tv_sec = timeout;
    tv.tv_usec = 0;

    // wait until timeout or data received
    n = select(s+1, &fds, NULL, NULL, &tv);
    if (n == 0) return -2; // timeout!
    if (n == -1) return -1; // error

    // data must be here, so do a normal recv()
    return recv(s, buf, len, 0);
}

// Sample call to recvtimeout():
    .
    .
    n = recvtimeout(s, buf, sizeof(buf), 10); // 10 second timeout

    if (n == -1) {
        // error occurred
        perror("recvtimeout");
    }
    else if (n == -2) {
        // timeout occurred
    } else {
        // got some data in buf
    }
    .
    . 

Notice that recvtimeout() returns -2 in case of a timeout. Why not return 0? Well, if you recall, a return value of 0 on a call to recv() means that the remote side closed the connection. So that return value is already spoken for, and -1 means "error", so I chose -2 as my timeout indicator.