Handle multiple socket connections
When writing server programs using sockets , it becomes necessary to handle multiple connections at a time , since a server needs to serve multiple clients.
There are many ways to do so. On linux this can be done in various ways like forking , threading , select method etc.
In this tutorial we shall use the select method approach. The select
function allows the program to monitor multiple sockets for a certain "activity" to occur. For example if there is some data to be read on one of the sockets select will provide that information.
fd_set
An fd_set is a set of sockets to "monitor" for some activity. There are four useful macros : FD_CLR, FD_ISSET, FD_SET, FD_ZERO for dealing with an fd_set.
FD_ZERO - Clear an fd_set FD_ISSET - Check if a descriptor is in an fd_set FD_SET - Add a descriptor to an fd_set FD_CLR - Remove a descriptor from an fd_set
//set of socket descriptors fd_set readfds; //socket to set FD_SET( s , &readfds);
select function
The select method takes a list of socket for monitoring them. Here is how :
activity = select( max_fd + 1 , &readfds , NULL , NULL , NULL);
The select function blocks , till an activity occurs. For example when a socket is ready to be read , select will return and readfs will have those sockets which are ready to be read.
Code
/** Handle multiple socket connections with select and fd_set on Linux */ #include <stdio.h> #include <string.h> //strlen #include <stdlib.h> #include <errno.h> #include <unistd.h> //close #include <arpa/inet.h> //close #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros #define TRUE 1 #define FALSE 0 #define PORT 8888 int main(int argc , char *argv[]) { int opt = TRUE; int master_socket , addrlen , new_socket , client_socket[30] , max_clients = 30 , activity, i , valread , sd; int max_sd; struct sockaddr_in address; char buffer[1025]; //data buffer of 1K //set of socket descriptors fd_set readfds; //a message char *message = "ECHO Daemon v1.0 \r\n"; //initialise all client_socket[] to 0 so not checked for (i = 0; i < max_clients; i++) { client_socket[i] = 0; } //create a master socket if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } //set master socket to allow multiple connections , this is just a good habit, it will work without this if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 ) { perror("setsockopt"); exit(EXIT_FAILURE); } //type of socket created address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons( PORT ); //bind the socket to localhost port 8888 if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0) { perror("bind failed"); exit(EXIT_FAILURE); } printf("Listener on port %d \n", PORT); //try to specify maximum of 3 pending connections for the master socket if (listen(master_socket, 3) < 0) { perror("listen"); exit(EXIT_FAILURE); } //accept the incoming connection addrlen = sizeof(address); puts("Waiting for connections ..."); while(TRUE) { //clear the socket set FD_ZERO(&readfds); //add master socket to set FD_SET(master_socket, &readfds); max_sd = master_socket; //add child sockets to set for ( i = 0 ; i < max_clients ; i++) { //socket descriptor sd = client_socket[i]; //if valid socket descriptor then add to read list if(sd > 0) FD_SET( sd , &readfds); //highest file descriptor number, need it for the select function if(sd > max_sd) max_sd = sd; } //wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL); if ((activity < 0) && (errno!=EINTR)) { printf("select error"); } //If something happened on the master socket , then its an incoming connection if (FD_ISSET(master_socket, &readfds)) { if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) { perror("accept"); exit(EXIT_FAILURE); } //inform user of socket number - used in send and receive commands printf("New connection , socket fd is %d , ip is : %s , port : %d \n" , new_socket , inet_ntoa(address.sin_addr) , ntohs(address.sin_port)); //send new connection greeting message if( send(new_socket, message, strlen(message), 0) != strlen(message) ) { perror("send"); } puts("Welcome message sent successfully"); //add new socket to array of sockets for (i = 0; i < max_clients; i++) { //if position is empty if( client_socket[i] == 0 ) { client_socket[i] = new_socket; printf("Adding to list of sockets as %d\n" , i); break; } } } //else its some IO operation on some other socket :) for (i = 0; i < max_clients; i++) { sd = client_socket[i]; if (FD_ISSET( sd , &readfds)) { //Check if it was for closing , and also read the incoming message if ((valread = read( sd , buffer, 1024)) == 0) { //Somebody disconnected , get his details and print 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 the socket and mark as 0 in list for reuse close( sd ); client_socket[i] = 0; } //Echo back the message that came in else { //set the string terminating NULL byte on the end of the data read buffer[valread] = '/** Handle multiple socket connections with select and fd_set on Linux */ #include <stdio.h> #include <string.h> //strlen #include <stdlib.h> #include <errno.h> #include <unistd.h> //close #include <arpa/inet.h> //close #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros #define TRUE 1 #define FALSE 0 #define PORT 8888 int main(int argc , char *argv[]) { int opt = TRUE; int master_socket , addrlen , new_socket , client_socket[30] , max_clients = 30 , activity, i , valread , sd; int max_sd; struct sockaddr_in address; char buffer[1025]; //data buffer of 1K //set of socket descriptors fd_set readfds; //a message char *message = "ECHO Daemon v1.0 \r\n"; //initialise all client_socket[] to 0 so not checked for (i = 0; i < max_clients; i++) { client_socket[i] = 0; } //create a master socket if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0) { perror("socket failed"); exit(EXIT_FAILURE); } //set master socket to allow multiple connections , this is just a good habit, it will work without this if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 ) { perror("setsockopt"); exit(EXIT_FAILURE); } //type of socket created address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons( PORT ); //bind the socket to localhost port 8888 if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0) { perror("bind failed"); exit(EXIT_FAILURE); } printf("Listener on port %d \n", PORT); //try to specify maximum of 3 pending connections for the master socket if (listen(master_socket, 3) < 0) { perror("listen"); exit(EXIT_FAILURE); } //accept the incoming connection addrlen = sizeof(address); puts("Waiting for connections ..."); while(TRUE) { //clear the socket set FD_ZERO(&readfds); //add master socket to set FD_SET(master_socket, &readfds); max_sd = master_socket; //add child sockets to set for ( i = 0 ; i < max_clients ; i++) { //socket descriptor sd = client_socket[i]; //if valid socket descriptor then add to read list if(sd > 0) FD_SET( sd , &readfds); //highest file descriptor number, need it for the select function if(sd > max_sd) max_sd = sd; } //wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely activity = select( max_sd + 1 , &readfds , NULL , NULL , NULL); if ((activity < 0) && (errno!=EINTR)) { printf("select error"); } //If something happened on the master socket , then its an incoming connection if (FD_ISSET(master_socket, &readfds)) { if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0) { perror("accept"); exit(EXIT_FAILURE); } //inform user of socket number - used in send and receive commands printf("New connection , socket fd is %d , ip is : %s , port : %d \n" , new_socket , inet_ntoa(address.sin_addr) , ntohs(address.sin_port)); //send new connection greeting message if( send(new_socket, message, strlen(message), 0) != strlen(message) ) { perror("send"); } puts("Welcome message sent successfully"); //add new socket to array of sockets for (i = 0; i < max_clients; i++) { //if position is empty if( client_socket[i] == 0 ) { client_socket[i] = new_socket; printf("Adding to list of sockets as %d\n" , i); break; } } } //else its some IO operation on some other socket :) for (i = 0; i < max_clients; i++) { sd = client_socket[i]; if (FD_ISSET( sd , &readfds)) { //Check if it was for closing , and also read the incoming message if ((valread = read( sd , buffer, 1024)) == 0) { //Somebody disconnected , get his details and print 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 the socket and mark as 0 in list for reuse close( sd ); client_socket[i] = 0; } //Echo back the message that came in else { //set the string terminating NULL byte on the end of the data read buffer[valread] = '\0'; send(sd , buffer , strlen(buffer) , 0 ); } } } } return 0; }'; send(sd , buffer , strlen(buffer) , 0 ); } } } } return 0; }
The source code has been put up on the following url
https://gist.github.com/silv3rm00n/5604330
Compile and run the above program. Then connect to it using telnet from 3 different terminals.
$ telnet localhost 8888
Now whatever you type and send to server will be send back as it is, or echoed.
The server terminal would show details of connections like this :
Waiting for connections... New connection , socket fd is 4 , ip is : 127.0.0.1 , port : 57831 Welcome message sent successfully Adding to list of sockets as 0 New connection , socket fd is 5 , ip is : 127.0.0.1 , port : 57832 Welcome message sent successfully Adding to list of sockets as 1 New connection , socket fd is 6 , ip is : 127.0.0.1 , port : 57833 Welcome message sent successfully Adding to list of sockets as 2 New connection , socket fd is 7 , ip is : 127.0.0.1 , port : 57834 Welcome message sent successfully
The client terminal can be like this
$ telnet localhost 8888 Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. ECHO Daemon v1.0 ccc ccc ddd ddd fff fff
There are other functions that can perform tasks similar to select. pselect , poll , ppoll
Resources
1. http://pubs.opengroup.org/onlinepubs/7908799/xsh/select.html
2. http://linux.die.net/man/2/select
How do I send a message to all connected clients?
Thanks for the sample cod, it works very well. I tried to modify it for udt, but now no incoming connections happen. Pls advise
Sir there is no other way to handle the multi client by telnet. I want to connect my client with this server code how i attach this
Well I suppose you’re the one who wrote this article on GeeksForGeeks http://www.geeksforgeeks.org/socket-programming-in-cc-handling-multiple-clients-on-server-without-multi-threading/
can i get the client code.
Can i get the client code please…
If you guys need a client code. Go to:
http://www.linuxhowtos.org/C_C++/socket.htm
The client code from there works
Thank you so much! Your the shit!
Code is commented very well so a beginner can understand it quickly.
Though there are some drawbacks which users pointed out, this how-to helped me a lot.
Thanks!
Hi, i want to know if this server can be used also for an UDP connection or only for the TCP
Thanks for everything
With the system call ‘select’ you can not handle multiple connections at a time. To do that, you need to use threads or processes.
Let’s suppose that a client request takes a long time to be processed. With ‘select’, during that time you can not attend to another client request until the first client request has finished
Correct. I greee. Publisher can clarify this.
What is your argument exactly? This tutorial is about handling multiple clients, using select function, which it does and it is correct.
Nice Tutorial :)
this was so helpful!
can you kindly email me a sample of 2client codes please.
it helped me alot.thanks
if server also write something time to time and want to broadcast the some messsage to the connected client then how it possible,,,please replay.
sir tanks in advace
Can you please provide the code for client?
Good Job, @silv3r_m00n:disqus , it helps…!
I can multi clients connect but when I send data from client 1 only client 1 receive response from server. How to server response to all client connected?
Thanx a lot for sharing!
Just one hint: If the client doesn’t close the socket-connection properly, the prog will exit when sending to this connection (line 171). “Program received signal SIGPIPE, Broken pipe.”
To avoid this You should use send() with the “MSG_NOSIGNAL” option:
send(sd , buffer , strlen(buffer) , MSG_NOSIGNAL );
Cheers, Stephan
it is bit unclear what will happen when client_socket array is full?
clients will be accepted using accept() but then they will appear nowhere?
I get the success on connect message multiple times from different telnet sessions – however I do not get anything echoed back to telnet client
The best explanation of sockets and select that I found, thanks!
i implemented such as this code, but when server send to client , client not recieved.why?!
is the client able to connect to server in the first place ?
does the server indicate that it received a new connection ?
if yes, then whatever message the client sends to server, it should get back the same and print it.
Nice article, but isn’t it a problem in the part “//add new socket to array of sockets” where you assign max_clients? I.e. you set “i = max_clients;” what if a client with socket number less than max_clients has disconnected earlier. Then in the loop you will find it in the client_socket[i] which is equal to zero. Then you will add the new socket in that location and assign max_clients to that socket. However there could be sockets with higher id still available.
i is just a temporary loop variable.
i = max_clients is used just to quit the loop, putting break instead of that would do the same thing.
and empty position in client_sockets array is searched everytime a new socket connection comes in and the loop starts i from 0.
Thanks!
You saved me MANY headaches.
wow, the article is perfect for me…
Thanks!
I go to this link while googling for “handling multiple socket connections”. This is exactly what I was looking for. Thanks!