Originally published on the old depletionmode / 2of1 blog (archived copy).
Two things I think you should know about linux sockets:
bind()s and SO_REUSEADDR
Firstly, the linux kernel will automatically lock up a port on bind() – even if the process is killed.
Well actually the kernel will close the socket when the process ends, however it stays in the 2MSL (2 x Max. Segment Lifetime) state for a few (2) minutes.
That means that the next time you try to bind() the port, you could get a message saying that it is already in use.
This is VERY annoying – especially if you’re trying to debug a TCP server for example.
The solution for this is to have your program instruct the kernel to allow a new socket to be bound to the same port. You can do this by setting the SO_REUSEADDR sockopt as follows:
int opt = 1;
setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof (opt))O_NONBLOCKing socket()s
The second thing that you should be aware of is that if you try to connect() to a server, there is no direct way to define a timeout for that connection attempt – meaning that you could be waiting for a LONG time.
There are two main solutions:
You could use alarm() and set up a relevant signal handler – but that is only really good for non-threaded applications.
The other way is to set the O_NONBLOCK flag on the socket file device. This will cause the connect() to always return a value pretty much immediately – usually EINPROGRESS. You can then use either select() or poll() (which is the much better option) to define a timeout for the connection.
Take a look at the following example:
#define POLLRDNORM 0x0040
#define POLLWRNORM 0x0100
#define TIMEOUT_SEC 5
int _nonblock_connect(struct sockaddr *addr, int addr_size) {
int socket_fd, flags, err;
socket_fd = socket(AF_INET, SOCK_STREAM, 0);
flags = fcntl(socket_fd, F_GETFL, NULL);
fcntl(socket_fd, F_SETFL, flags | O_NONBLOCK);
err = connect(socket_fd, addr, addr_size);
if ((errno == EINPROGRESS) || (err == 0)) {
struct pollfd pfd;
pfd.fd = socket_fd;
pfd.events = POLLWRNORM | POLLRDNORM;
if (poll(&pfd, 1, TIMEOUT_SEC * 1000) == 0) {
fcntl(socket_fd, F_SETFL, flags); // restore blocking mode
return socket_fd; // connection successful
} else
err = -1; // connection timed out after TIMEOUT_SEC
}
close(socket_fd);
return err;
}