Handle multiple socket connections with fd_set and select on Linux

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. Theselect 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

1 //set of socket descriptors
2 fd_set readfds;
3  
4 //socket to set
5 FD_SET( s , &readfds);

select

The select method takes a list of socket for monitoring them. Here is how :

1 activity = select( max_clients , &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

1 /*
2  * @brief
3  * manage multiple connections with FD_SET
4  *
5  * @author Silver Moon
6  *
7  * */
8  
9 #include <stdio.h>
10 #include <string.h>   //strlen
11 #include <stdlib.h>
12 #include <errno.h>
13 #include <unistd.h>   //close
14 #include <arpa/inet.h>    //close
15 #include <sys/types.h>
16 #include <sys/socket.h>
17 #include <netinet/in.h>
18 #include <sys/time.h> //FD_SET, FD_ISSET, FD_ZERO macros
19  
20 #define TRUE   1
21 #define FALSE  0
22  
23 int main(int argc , char *argv[])
24 {
25     int opt = TRUE;
26     int master_socket , addrlen , new_socket , client_socket[30] , max_clients = 30 , activity, i , valread , s;
27     struct sockaddr_in address;
28      
29     char buffer[1025];  //data buffer of 1K
30      
31     //set of socket descriptors
32     fd_set readfds;
33      
34     //a message
35     char *message = "ECHO Daemon v1.0 \r\n";
36  
37     //initialise all client_socket[] to 0 so not checked
38     for (i = 0; i < max_clients; i++)
39     {
40         client_socket[i] = 0;
41     }
42      
43     //create a master socket
44     if( (master_socket = socket(AF_INET , SOCK_STREAM , 0)) == 0)
45     {
46         perror("socket failed");
47         exit(EXIT_FAILURE);
48     }
49  
50     //set master socket to allow multiple connections , this is just a good habit, it will work without this
51     if( setsockopt(master_socket, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt)) < 0 )
52     {
53         perror("setsockopt");
54         exit(EXIT_FAILURE);
55     }
56  
57     //type of socket created
58     address.sin_family = AF_INET;
59     address.sin_addr.s_addr = INADDR_ANY;
60     address.sin_port = htons( 8888 );
61      
62     //bind the socket to localhost port 8888
63     if (bind(master_socket, (struct sockaddr *)&address, sizeof(address))<0)
64     {
65         perror("bind failed");
66         exit(EXIT_FAILURE);
67     }
68  
69     //try to specify maximum of 3 pending connections for the master socket
70     if (listen(master_socket, 3) < 0)
71     {
72         perror("listen");
73         exit(EXIT_FAILURE);
74     }
75      
76     //accept the incoming connection
77     addrlen = sizeof(address);
78     puts("Waiting for connections...");
79     while(TRUE)
80     {
81         //clear the socket set
82         FD_ZERO(&readfds);
83  
84         //add master socket to set
85         FD_SET(master_socket, &readfds);
86          
87         //add child sockets to set
88         for ( i = 0 ; i < max_clients ; i++)
89         {
90             s = client_socket[i];
91             if(s > 0)
92             {
93                 FD_SET( s , &readfds);
94             }
95         }
96  
97         //wait for an activity on one of the sockets , timeout is NULL , so wait indefinitely
98         activity = select( max_clients + 3 , &readfds , NULL , NULL , NULL);
99    
100         if ((activity < 0) && (errno!=EINTR))
101         {
102             printf("select error");
103         }
104          
105         //If something happened on the master socket , then its an incoming connection
106         if (FD_ISSET(master_socket, &readfds))
107         {
108             if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen))<0)
109             {
110                 perror("accept");
111                 exit(EXIT_FAILURE);
112             }
113          
114             //inform user of socket number - used in send and receive commands
115             printf("New connection , socket fd is %d , ip is : %s , port : %d \n" , new_socket , inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
116        
117             //send new connection greeting message
118             if( send(new_socket, message, strlen(message), 0) != strlen(message) )
119             {
120                 perror("send");
121             }
122              
123             puts("Welcome message sent successfully");
124              
125             //add new socket to array of sockets
126             for (i = 0; i < max_clients; i++)
127             {
128                 s = client_socket[i];
129                 if (s == 0)
130                 {
131                     client_socket[i] = new_socket;
132                     printf("Adding to list of sockets as %d\n" , i);
133                     i = max_clients;
134                 }
135             }
136         }
137          
138         //else its some IO operation on some other socket :)
139         for (i = 0; i < max_clients; i++)
140         {
141             s = client_socket[i];
142              
143             if (FD_ISSET( s , &readfds))
144             {
145                 //Check if it was for closing , and also read the incoming message
146                 if ((valread = read( s , buffer, 1024)) == 0)
147                 {
148                     //Somebody disconnected , get his details and print
149                     getpeername(s , (struct sockaddr*)&address , (socklen_t*)&addrlen);
150                     printf("Host disconnected , ip %s , port %d \n" , inet_ntoa(address.sin_addr) , ntohs(address.sin_port));
151                      
152                     //Close the socket and mark as 0 in list for reuse
153                     close( s );
154                     client_socket[i] = 0;
155                 }
156                  
157                 //Echo back the message that came in
158                 else
159                 {
160                     //set the terminating NULL byte on the end of the data read
161                     buffer[valread] = '\0';
162                     send( s , buffer , strlen(buffer) , 0 );
163                 }
164             }
165         }
166     }
167      
168     return 0;
169 }

Compile and run the above program. Then connect to it using telnet from 3 different terminals.

1 $ 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

在MySQL 8.0中,可以通过配置max_connections和innodb_buffer_pool_size来优化性能。下面是如何配置这些参数的方法: 1. max_connections:这个参数用于设置MySQL服务器允许的最大连接数。默认值为151。可以根据应用程序的需求适当调整这个值。 - 打开MySQL的配置文件my.cnf或my.ini文件(根据操作系统不同,文件位置可能有所不同)。 - 在[mysqld]部分中,添加或修改max_connections参数,例如: ``` [mysqld] max_connections = 200 ``` - 保存文件并重启MySQL服务器,使新的设置生效。 2. innodb_buffer_pool_size:这个参数用于设置InnoDB存储引擎使用的缓冲池大小,即用于缓存数据和索引的内存大小。默认值为134217728(即128MB)。 - 打开MySQL的配置文件my.cnf或my.ini文件。 - 在[mysqld]部分中,添加或修改innodb_buffer_pool_size参数,例如: ``` [mysqld] innodb_buffer_pool_size = 1G ``` 这里的1G表示1GB内存大小。 - 保存文件并重启MySQL服务器,使新的设置生效。 注意事项: - 调整max_connections时需要考虑服务器的硬件资源和内存限制。不宜设置过高,以免导致服务器负载过大或内存不足。 - 调整innodb_buffer_pool_size时需要根据服务器的可用内存进行设置。建议将大部分可用内存分配给InnoDB缓冲池,但不要超过系统的物理内存限制。 在进行以上配置时,请确保备份了MySQL的配置文件,并谨慎操作,以避免对数据库产生不良影响。如有需要,建议在进行配置之前参考MySQL官方文档或咨询专业人士的意见。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值