The select function can communicate with multiple sockets in a non-blocking manner.ProgramIt only demonstrates the use of the select function. The function is very simple. Even if a connection is closed, the current number of connections is not modified. When the number of connections reaches the maximum value, the program is terminated.
1. The program uses an array fd_a. After the communication starts, multiple socket descriptors to be communicated are put into this array.
2. Generate a socket descriptor named sock_fd for listening to the port.
3. Put the descriptor not 0 in sock_fd and array fd_a into the fdsr set to be checked by select.
4. Processes connections that can receive data in fdsr. If it is sock_fd, it indicates that a new connection is added, and the socket descriptor of the new connection is placed in fd_a.
# Include <stdio. h>
# Include <stdlib. h>
# Include <unistd. h>
# Include <errno. h>
# Include <string. h>
# Include <sys/types. h>
# Include <sys/socket. h>
# Include <netinet/in. h>
# Include <ARPA/inet. h>
# Define myport 1234 // The port users will be ing
# Define backlog 5 // how many pending connections queue will hold
# Define buf_size 200
Int fd_a [backlog]; // accepted connection FD
Int conn_amount; // current connection amount
Void showclient ()
{
Int I;
Printf ("client amount: % d \ n", conn_amount );
For (I = 0; I <backlog; I ++ ){
Printf ("[% d]: % d", I, fd_a [I]);
}
Printf ("\ n ");
}
Int main (void)
{
Int sock_fd, new_fd; // listen on sock_fd, new connection on new_fd
Struct sockaddr_in server_addr; // server address information
Struct sockaddr_in client_addr; // connector's address information
Socklen_t sin_size;
Int Yes = 1;
Char Buf [buf_size];
Int ret;
Int I;
If (sock_fd = socket (af_inet, sock_stream, 0) =-1 ){
Perror ("socket ");
Exit (1 );
}
If (setsockopt (sock_fd, sol_socket, so_reuseaddr, & yes, sizeof (INT) =-1 ){
Perror ("setsockopt ");
Exit (1 );
}
Server_addr.sin_family = af_inet; // host byte order
Server_addr.sin_port = htons (myport); // short, network byte order
Server_addr.sin_addr.s_addr = inaddr_any; // automatically fill with my IP
Memset (server_addr.sin_zero, '\ 0', sizeof (server_addr.sin_zero ));
If (BIND (sock_fd, (struct sockaddr *) & server_addr, sizeof (server_addr) =-1 ){
Perror ("bind ");
Exit (1 );
}
If (Listen (sock_fd, backlog) =-1 ){
Perror ("listen ");
Exit (1 );
}
Printf ("Listen port % d \ n", myport );
Fd_set fdsr;
Int maxsock;
Struct timeval TV;
Conn_amount = 0;
Sin_size = sizeof (client_addr );
Maxsock = sock_fd;
While (1 ){
// Initialize file descriptor set
Fd_zero (& fdsr );
Fd_set (sock_fd, & fdsr );
// Timeout setting
TV. TV _sec = 30;
TV. TV _usec = 0;
// Add active connection to fd set
for (I = 0; I If (fd_a [I]! = 0) {
fd_set (fd_a [I], & fdsr);
}< BR >}
Ret = select (maxsock + 1, & fdsr, null, null, & TV );
If (Ret <0 ){
Perror ("select ");
Break;
} Else if (ret = 0 ){
Printf ("timeout \ n ");
Continue;
}
// Check every FD in the Set
For (I = 0; I <conn_amount; I ++ ){
If (fd_isset (fd_a [I], & fdsr )){
Ret = Recv (fd_a [I], Buf, sizeof (BUF), 0 );
If (Ret <= 0) {// client close
Printf ("client [% d] Close \ n", I );
Close (fd_a [I]);
Fd_clr (fd_a [I], & fdsr );
Fd_a [I] = 0;
} Else {// receive data
If (Ret <buf_size)
Memset (& Buf [RET], '\ 0', 1 );
Printf ("client [% d] Send: % s \ n", I, Buf );
}
}
}
// Check whether a new connection comes
If (fd_isset (sock_fd, & fdsr )){
New_fd = accept (sock_fd, (struct sockaddr *) & client_addr, & sin_size );
If (new_fd <= 0 ){
Perror ("accept ");
Continue;
}
// Add to fd queue
If (conn_amount <backlog ){
Fd_a [conn_amount ++] = new_fd;
Printf ("New Connection Client [% d] % s: % d \ n", conn_amount,
Inet_ntoa (client_addr.sin_addr), ntohs (client_addr.sin_port ));
If (new_fd> maxsock)
Maxsock = new_fd;
}
Else {
Printf ("Max connections arrive, exit \ n ");
Send (new_fd, "bye", 4, 0 );
Close (new_fd );
Break;
}
}
Showclient ();
}
// Close other connections
For (I = 0; I <backlog; I ++ ){
If (fd_a [I]! = 0 ){
Close (fd_a [I]);
}
}
Exit (0 );
}
The landlord is really amazing. Select is really clever! Full implementation of multi-user communication in the case of a single thread, I have runCode, Very successful! However, I think that fd_a [conn_amount ++] = new_fd IN THE accept function can be slightly improved. According to the intention of the landlord, when a user continuously disconnects and then disconnects, when the number of connections exceeds the value of maxconnection, the system will exit. Therefore, fd_a [I] is not well utilized and cannot achieve dynamic management. We recommend that you only use conn_amount as the number of client connections, instead of adding connections, add 1 when accept is successful, and reduce 1 when Recv = 0. We recommend that you set fd_a [conn_amount ++] = new_fd; this program is changed
For (I = 0; I <maxclient; I ++)
{
If (FD [I] = 0)
{
FD [I] = new_fd;
Break;
}
}
Conn_amount ++;
In this way, the space of FD [I] can be reused;
In addition, when the Recv return value is <= 0, add conn_amount ++;
Another point is that when the maximum number of connections is exceeded, the break should be "continue", which will be more user-friendly. The client will close too many requests and there is no need to self-destroy them, in this way, the entire system can be dynamically connected to the client. Thanks to the contribution of the landlord, I have made a lot of detours. Now, on the basis of the landlord, I have basically implemented a server-side program for multi-user access and added a database. I think this world is so wonderful!
I haven't touched it for more than a month, but I heard that select has such a big role and I am very excited. At that time, I wanted to implement Single-threaded Multi-user operations, just as select provided all of this, because the entire project has a lot of content, it also includes the database and QT interface, so there will be functions or keywords like qdebug and emit, but this does not affect reading. Friends can use functions such as printf as needed to replace or remove them, only the socket connection section is provided here. Some of the following functions may be macro-defined, such as buf_size and maxclient. In addition to implementing dynamic management of the maximum number of connections, this function also limits the idle connection time (max_idleconnctime) of a connection. As you can see, the following reads the system time, which is the connection control, the purpose is to disable the connection when a user does not connect to a request within a certain period of time, nor sends or accepts data, this also effectively stops some unconventional disconnection methods, such as sudden power failure on the client and network disconnection (at this time, clients that cannot be detected by the server are disconnected ).
These codes have been tested for a long time without any problems. If you have any more concise and efficient methods, you must share them!
Void run ()
{
Char MSG [buf_size];
Int listen_socket, RET, on;
Struct sockaddr_in local_addr;
Struct sockaddr_in client_addr;
Int I;
Fd_set fdsr; // file descriptor set definition
Socklen_t addr_size;
Addr_size = sizeof (struct sockaddr_in );
Int conn_amount = 0; // The maximum number of active connections currently
Int new_fd;
Struct timeval TV;
// Create a Socket socket
If (listen_socket = socket (af_inet, sock_stream, ipproto_tcp) =-1)
{
Emit err_msg_signal ("failed create socket ");
}
// Bind API function will allow immediate address reuse
On = 1;
Ret = setsockopt (listen_socket, sol_socket, so_reuseaddr,
& On, sizeof (on ));
Int nnettimeout = 2000; // 2 seconds
// Set the sending time limit
Setsockopt (listen_socket, sol_socket, so_sndtimeo, (char *) & nnettimeout, sizeof (INT ));
// Set the receiving time limit
Setsockopt (listen_socket, sol_socket, so_rcvtimeo, (char *) & nnettimeout, sizeof (INT ));
// Set the local service type
Local_addr.sin_family = af_inet;
Local_addr.sin_port = htons (port );
Local_addr.sin_addr.s_addr = inaddr_any;
// While (flag_port = 0)
// Bind the local IP address and port number
If (BIND (listen_socket, (struct sockaddr *) & local_addr, sizeof (struct sockaddr) =-1)
{
Emit err_msg_signal ("failed bind ");
}
// Listen for client connection
If (Listen (listen_socket, 8) =-1)
{
Emit err_msg_signal ("failed listen ");
}
Qtime current_time;
Current_time = qtime: currenttime ();
Int flag_minutechange = 0, lastminute = current_time.currenttime (). Minute ();
Int maxsock = listen_socket;
/***************************************
The following are the key parts of the system for concurrent connection processing.
***************************************/
While (1)
{
If (current_time.currenttime (). Minute ()! = Lastminute) // reads the system time from the beginning of each cycle. Compared with the number of minutes in the previous cycle, this provides a basis for the following timeout judgments.
{
Lastminute = current_time.currenttime (). Minute ();
Flag_minutechange = 1;
}
Fd_zero (& fdsr); // the descriptor set is rebuilt every time a loop is entered.
Fd_set (listen_socket, & fdsr );
For (I = 0; I <maxclient; I ++) // Add existing sockets to the descriptor set
{
If (FD [I]! = 0)
{
Fd_set (FD [I], & fdsr );
If (flag_minutechange = 1)
{
Con_time [I] --;
If (con_time [I] <= 0)
{
Close (FD [I]);
Fd_clr (FD [I], & fdsr );
FD [I] = 0;
Conn_amount --;
}
}
}
}
Flag_minutechange = 0;
TV. TV _sec = 1;
TV. TV _usec = 0;
Ret = select (maxsock + 1, & fdsr, null, null, & TV); // key select () function, used to detect exceptions of each socket
// If there is a connection request or send request in the file descriptor set, it will be processed accordingly,
// Thus, the process blocking in the case of a single thread is successfully solved, and multi-user connection and communication are realized.
If (Ret <0) // <0 indicates that the test failed.
{
Qdebug () <"failed select \ n ";
Break;
}
Else if (ret = 0) // = 0 indicates timeout.
{
// Qdebug () <"timeout \ n ";
Continue;
}
// if an exception is found in the SELECT statement, cyclically judge whether data arrives for each active connection.
for (I = 0; I {< br> If (fd_isset (FD [I], & fdsr)
{< br> ret = Recv (FD [I], MSG, buf_size, 0);
If (Ret <= 0) // Recv <= 0, indicating that the client closes the connection and the server also closes the connection, remove the connector from the file descriptor set
{< br> qdebug ("client [% d] Close \ n", I );
close (FD [I]);
fd_clr (FD [I], & fdsr);
FD [I] = 0;
conn_amount --;
}< br> else // otherwise, the client sends data, handle the request
{< br> con_time [I] = max_idleconnctime; // The number of times out for re-writing FD [I, after that, if max_idleconnctime does not respond to this connection within minutes, the server will close the connection
If (Ret emit err_msg_signal ("Client IP:" + qstring:: fromlatin1 (inet_ntoa (client_addr.sin_addr) +
"port:" + qstring: Number (ntohs (client_addr.sin_port) + "coming data ");
qdebug ("client [% d] Send: % s \ n", I, MSG);
MSG [RET] = '\ 0 ';
emit recv_msg_signal (qstring: fromlatin1 (MSG), FD [I]);
// send (FD [I], MSG, RET, 0 );
}< BR >}
// The following indicates that a connection request from the client is abnormal.
If (fd_isset (listen_socket, & fdsr ))
{
New_fd = accept (listen_socket, (struct sockaddr *) & client_addr, & addr_size );
If (new_fd <= 0)
{
Qdebug ("failed accept ");
Continue;
}
// Determine whether the number of active connections is smaller than the maximum number of connections. If yes, add a new connection to the file descriptor set.
If (conn_amount <maxclient)
{
For (I = 0; I <maxclient; I ++)
{
If (FD [I] = 0)
{
FD [I] = new_fd;
Con_time [I] = max_idleconnctime; // set the timeout value for each new connection. If max_idleconnctime does not respond within minutes after the connection is established, close the connection.
Break;
}
}
Conn_amount ++;
// FD [conn_amount ++] = new_fd;
Qdebug ("New Connection Client [% d] % s: % d \ n", conn_amount,
Inet_ntoa (client_addr.sin_addr), ntohs (client_addr.sin_port ));
Emit err_msg_signal ("Client IP:" + qstring: fromlatin1 (inet_ntoa (client_addr.sin_addr) +
"Port:" + qstring: Number (ntohs (client_addr.sin_port )));
If (new_fd> maxsock)
Maxsock = new_fd;
}
Else
{
Qdebug ("maxclient arrive, exit \ n ");
Send (new_fd, "Over maxclient \ n", 25, 0 );
Close (new_fd );
Continue;
}
}
}
}