Jun
08
accept()ing multiple tcp connections
Filed Under (C, Coding, Networking) by 2of1 on 08-06-2010
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)--; }



