Multiple Clients with Select
Managing multiple clients efficiently is a common challenge in network programming. Using threads for each client can quickly become resource-intensive and complex. Instead, you can use the select() system call to monitor multiple file descriptors, such as sockets, for readiness to perform I/O operations. This allows a single-threaded server to handle multiple clients simultaneously. With select(), you can check which sockets are ready to read, write, or have errors, and then act on only those, avoiding the need to block on one client while others wait.
server_select.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <arpa/inet.h> #include <sys/select.h> #define PORT 8080 #define MAX_CLIENTS FD_SETSIZE #define BUFFER_SIZE 1024 int main() { int listen_fd, new_fd, max_fd, activity, i, valread, sd; int client_socket[MAX_CLIENTS]; struct sockaddr_in address; char buffer[BUFFER_SIZE]; // Initialize all client_socket[] to 0 for (i = 0; i < MAX_CLIENTS; i++) client_socket[i] = 0; // Create listening socket if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // Prepare address address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // Bind if (bind(listen_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // Listen if (listen(listen_fd, 5) < 0) { perror("listen"); exit(EXIT_FAILURE); } printf("Server listening on port %d\n", PORT); fd_set readfds; while (1) { // Clear and set file descriptors FD_ZERO(&readfds); FD_SET(listen_fd, &readfds); max_fd = listen_fd; // Add client sockets to set for (i = 0; i < MAX_CLIENTS; i++) { sd = client_socket[i]; if (sd > 0) FD_SET(sd, &readfds); if (sd > max_fd) max_fd = sd; } // Wait for activity activity = select(max_fd + 1, &readfds, NULL, NULL, NULL); if ((activity < 0) && (errno != EINTR)) { perror("select error"); } // Incoming connection if (FD_ISSET(listen_fd, &readfds)) { socklen_t addrlen = sizeof(address); if ((new_fd = accept(listen_fd, (struct sockaddr *)&address, &addrlen)) < 0) { perror("accept"); exit(EXIT_FAILURE); } printf("New connection: socket fd %d, IP: %s, port: %d\n", new_fd, inet_ntoa(address.sin_addr), ntohs(address.sin_port)); // Add new socket to array for (i = 0; i < MAX_CLIENTS; i++) { if (client_socket[i] == 0) { client_socket[i] = new_fd; break; } } } // IO on client sockets for (i = 0; i < MAX_CLIENTS; i++) { sd = client_socket[i]; if (FD_ISSET(sd, &readfds)) { if ((valread = read(sd, buffer, BUFFER_SIZE)) == 0) { // Client disconnected getpeername(sd, (struct sockaddr *)&address, (socklen_t *)&addrlen); printf("Host disconnected: IP %s, port %d\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port)); close(sd); client_socket[i] = 0; } else { buffer[valread] = '\0'; printf("Received from client %d: %s\n", sd, buffer); send(sd, buffer, valread, 0); // Echo back } } } } return 0; }
Thanks for your feedback!
Ask AI
Ask AI
Ask anything or try one of the suggested questions to begin our chat
Can you explain how the select() system call works in more detail?
Can you provide a simple example of using select() in a server?
What are the limitations of using select() for handling multiple clients?
Awesome!
Completion rate improved to 8.33
Multiple Clients with Select
Swipe to show menu
Managing multiple clients efficiently is a common challenge in network programming. Using threads for each client can quickly become resource-intensive and complex. Instead, you can use the select() system call to monitor multiple file descriptors, such as sockets, for readiness to perform I/O operations. This allows a single-threaded server to handle multiple clients simultaneously. With select(), you can check which sockets are ready to read, write, or have errors, and then act on only those, avoiding the need to block on one client while others wait.
server_select.c
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/types.h> #include <arpa/inet.h> #include <sys/select.h> #define PORT 8080 #define MAX_CLIENTS FD_SETSIZE #define BUFFER_SIZE 1024 int main() { int listen_fd, new_fd, max_fd, activity, i, valread, sd; int client_socket[MAX_CLIENTS]; struct sockaddr_in address; char buffer[BUFFER_SIZE]; // Initialize all client_socket[] to 0 for (i = 0; i < MAX_CLIENTS; i++) client_socket[i] = 0; // Create listening socket if ((listen_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } // Prepare address address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(PORT); // Bind if (bind(listen_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { perror("bind failed"); exit(EXIT_FAILURE); } // Listen if (listen(listen_fd, 5) < 0) { perror("listen"); exit(EXIT_FAILURE); } printf("Server listening on port %d\n", PORT); fd_set readfds; while (1) { // Clear and set file descriptors FD_ZERO(&readfds); FD_SET(listen_fd, &readfds); max_fd = listen_fd; // Add client sockets to set for (i = 0; i < MAX_CLIENTS; i++) { sd = client_socket[i]; if (sd > 0) FD_SET(sd, &readfds); if (sd > max_fd) max_fd = sd; } // Wait for activity activity = select(max_fd + 1, &readfds, NULL, NULL, NULL); if ((activity < 0) && (errno != EINTR)) { perror("select error"); } // Incoming connection if (FD_ISSET(listen_fd, &readfds)) { socklen_t addrlen = sizeof(address); if ((new_fd = accept(listen_fd, (struct sockaddr *)&address, &addrlen)) < 0) { perror("accept"); exit(EXIT_FAILURE); } printf("New connection: socket fd %d, IP: %s, port: %d\n", new_fd, inet_ntoa(address.sin_addr), ntohs(address.sin_port)); // Add new socket to array for (i = 0; i < MAX_CLIENTS; i++) { if (client_socket[i] == 0) { client_socket[i] = new_fd; break; } } } // IO on client sockets for (i = 0; i < MAX_CLIENTS; i++) { sd = client_socket[i]; if (FD_ISSET(sd, &readfds)) { if ((valread = read(sd, buffer, BUFFER_SIZE)) == 0) { // Client disconnected getpeername(sd, (struct sockaddr *)&address, (socklen_t *)&addrlen); printf("Host disconnected: IP %s, port %d\n", inet_ntoa(address.sin_addr), ntohs(address.sin_port)); close(sd); client_socket[i] = 0; } else { buffer[valread] = '\0'; printf("Received from client %d: %s\n", sd, buffer); send(sd, buffer, valread, 0); // Echo back } } } } return 0; }
Thanks for your feedback!