Originally published on the old depletionmode / 2of1 blog (archived copy).
The best way I’ve found of handling multiple tcp connections on the same socket is to use a poll file device with the listen()ing socket itself as the first pfd.
The example below is based on a i5/OS example available from IBM at http://publib.boulder.ibm.com/infocenter/iseries/v6r1m0/index.jsp?topic=/rzab6/example.htm.
I’ve made some minor changes to how flags are set to allow the example below to work on Linux/Win/BSD.
(Please note the lack of error checking in the code below. You should obviously have some in your application.)
#include ...
#include <poll.h>
#include <fcntl.h>
int listen_sd, cli_len, npfds = 1, port = 12345, max_connections = 5, one = 1;
struct sockaddr_in srv;
struct pollfd pfds[100];
memset(pfds, 0, sizeof(pfds));
memset(&srv, 0, sizeof(srv);
listen_sd = socket(AF_INET, SOCK_STREAM, 0); // set up socket
setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); // allow for port reuse
srv.sin_family = AF_INET;
srv.sin_addr.s_addr = INADDR_ANY;
srv.sin_port = htons(port);
fcntl(listen_sd, FSETFL, O_NONBLOCK); // make sure socket doesn't block
bind(listen_sd, (struct sockaddr *)&srv, sizeof(srv)); // bind
listen(listen_sd, max_connections); // listens for a max of 5 connections
pfds[0].fd = listen_fd; // set up the first pfd to be the listen()ing socket
pfds[0].events = POLLIN; // trigger when POLLIN event
while (1) {
if (poll(pfds, npfds, -1) > 0) { // there's an event waiting
int i, j = npfds;
for(i = 0; i < j; i++) { // process each fd
if (pfds[i].revents == 0) continue; // this fd has nothing to process, so move to next
if (pfds[i].fd == listen_sd) { // event has been received on the listening socket
int fd;
do {
int flags;
fd = accept(listen_sd, NULL, NULL);
flags = fcntl(fd, F_GETFL, NULL) | ~O_NONBLOCK;
fcntl(fd, F_SETFL, flags); // make sure fd doesn't block
// set up the new pfd
pfds[npfds].fd = fd;
pfds[npfds].events = POLLIN;
npfds++;
} while (fd != -1);
} else { // there is input waiting on the current connection fd
unsigned char buf[1000];
if (recv(pfds[i].fd, buf, 1000) > 0) {
// process data
} else { // connection has been closed
close(pfds[i].fd);
remove_pfd_from_array(pfds, &npfds, i); // make sure pfd is no longer polled
}
}
}
}The remove_pfd_from_array() removes the disconnected fd from the array. There are better ways of doing this, but a dirty implementation looks something like this:
void remove_pfd_from_array(struct pollfd *pfds, int *npfds, int fd)
{
int i;
for (i = 0; i < *npfds - 1; i++)
pfds[i] = pfds[i + 1];
memset(pfds + i, 0, sizeof(struct pollfd));
(*npfds)--;
}