// https://www.ibm.com/docs/en/i/7.2?topic=designs-using-poll-instead-select # include // close # include // exit # include // cout, cin # include // errno # include // perror # include // memset # include // socket, accept, listen, send, recv, bind, connect, setsockopt, getsockname # include // sockaddr_in # include // inet_ntoa, inet_addr, htonl, htons, ntohl, ntohs # include // poll # include // fcntl # include // ioctl # define FALSE 0 # define TRUE 1 # define SERVER_PORT 4040 int main () { int len, rc, on = 1; int listen_sd = -1, new_sd = -1; int end_server = FALSE, compress_array = FALSE; int close_conn; char buffer[80]; struct sockaddr_in addr; struct pollfd fds[200]; // struct pollfd fds[1]; // struct pollfd fds; int nfds = 1, current_size = 0, i, j; // ************************************************************* // * Create an AF_INET stream socket to receive incoming * // * connections on * // ************************************************************* listen_sd = socket(AF_INET, SOCK_STREAM, 0); if (listen_sd < 0) { perror("socket() failed"); exit(-1); } // ************************************************************* // * Allow socket descriptor to be reuseable * // ************************************************************* rc = setsockopt(listen_sd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)); if (rc < 0) { perror("setsockopt() failed"); close(listen_sd); exit(-1); } // ************************************************************* // * Set socket to be nonblocking. All of the sockets for * // * the incoming connections will also be nonblocking since * // * they will inherit that state from the listening socket. * // ************************************************************* rc = ioctl(listen_sd, FIONBIO, (char *)&on); if (rc < 0) { perror("ioctl() failed"); close(listen_sd); exit(-1); } // ************************************************************* // * Bind the socket * // ************************************************************* memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; // memcpy(&addr.sin_addr, &inaddr_any, sizeof(inaddr_any)); addr.sin_addr.s_addr = htonl(INADDR_ANY); addr.sin_port = htons(SERVER_PORT); rc = bind(listen_sd, (struct sockaddr *)&addr, sizeof(addr)); if (rc < 0) { perror("bind() failed"); close(listen_sd); exit(-1); } // ************************************************************* // * Set the listen back log * // ************************************************************* rc = listen(listen_sd, 32); if (rc < 0) { perror("listen() failed"); close(listen_sd); exit(-1); } // ************************************************************* // * Initialize the pollfd structure * // ************************************************************* memset(fds, 0 , sizeof(fds)); // ************************************************************* // * Set up the initial listening socket * // ************************************************************* fds[0].fd = listen_sd; fds[0].events = POLLIN; // ************************************************************* // * Loop waiting for incoming connects or for incoming data * // * on any of the connected sockets. * // ************************************************************* do { // *********************************************************** // * Call poll() * // *********************************************************** poll(fds, nfds, -1); // *********************************************************** // * One or more descriptors are readable. Need to * // * determine which ones they are. * // *********************************************************** current_size = nfds; for (i = 0; i < current_size; i++) { // ********************************************************* // * Loop through to find the descriptors that returned * // * POLLIN and determine whether it's the listening * // * or the active connection. * // ********************************************************* if(fds[i].revents == 0) continue; // ********************************************************* // * If revents is not POLLIN, it's an unexpected result, * // * log and end the server. * // ********************************************************* if(fds[i].revents != POLLIN) { printf(" Error! revents = %d\n", fds[i].revents); end_server = TRUE; break; } if (fds[i].fd == listen_sd) { // ******************************************************* // * Listening descriptor is readable. * // ******************************************************* printf(" Listening socket is readable\n"); // ******************************************************* // * Accept all incoming connections that are * // * queued up on the listening socket before we * // * loop back and call poll again. * // ******************************************************* do { // ***************************************************** // * Accept each incoming connection. If * // * accept fails with EWOULDBLOCK, then we * // * have accepted all of them. Any other * // * failure on accept will cause us to end the * // * server. * // ***************************************************** new_sd = accept(listen_sd, NULL, NULL); if (new_sd < 0) { if (errno != EWOULDBLOCK) { perror(" accept() failed"); end_server = TRUE; } break; } // ***************************************************** // * Add the new incoming connection to the * // * pollfd structure * // ***************************************************** printf(" New incoming connection - %d\n", new_sd); fds[nfds].fd = new_sd; fds[nfds].events = POLLIN; nfds++; // ***************************************************** // * Loop back up and accept another incoming * // * connection * // ***************************************************** } while (new_sd != -1); } // ********************************************************* // * This is not the listening socket, therefore an * // * existing connection must be readable * // ********************************************************* else { printf(" Descriptor %d is readable\n", fds[i].fd); close_conn = FALSE; // ******************************************************* // * Receive all incoming data on this socket * // * before we loop back and call poll again. * // ******************************************************* do { // ***************************************************** // * Receive data on this connection until the * // * recv fails with EWOULDBLOCK. If any other * // * failure occurs, we will close the * // * connection. * // ***************************************************** rc = recv(fds[i].fd, buffer, sizeof(buffer), 0); if (rc < 0) { if (errno != EWOULDBLOCK) { perror(" recv() failed"); close_conn = TRUE; } break; } // ***************************************************** // * Check to see if the connection has been * // * closed by the client * // ***************************************************** if (rc == 0) { printf(" Connection closed\n"); close_conn = TRUE; break; } // ***************************************************** // * Data was received * // ***************************************************** len = rc; printf(" %d bytes received\n", len); // ***************************************************** // * Echo the data back to the client * // ***************************************************** rc = send(fds[i].fd, buffer, len, 0); if (rc < 0) { perror(" send() failed"); close_conn = TRUE; break; } } while(TRUE); // ******************************************************* // * If the close_conn flag was turned on, we need * // * to clean up this active connection. This * // * clean up process includes removing the * // * descriptor. * // ******************************************************* if (close_conn) { close(fds[i].fd); fds[i].fd = -1; compress_array = TRUE; } } // End of existing connection is readable } // End of loop through pollable descriptors // *********************************************************** // * If the compress_array flag was turned on, we need * // * to squeeze together the array and decrement the number * // * of file descriptors. We do not need to move back the * // * events and revents fields because the events will always* // * be POLLIN in this case, and revents is output. * // *********************************************************** if (compress_array) { compress_array = FALSE; for (i = 0; i < nfds; i++) { if (fds[i].fd == -1) { for(j = i; j < nfds; j++) { fds[j].fd = fds[j+1].fd; } i--; nfds--; } } } } while (end_server == FALSE); // End of serving running. // ************************************************************* // * Clean up all of the sockets that are open * // ************************************************************* for (i = 0; i < nfds; i++) { if(fds[i].fd >= 0) close(fds[i].fd); } }