1. Basic Concepts
IO multiplexing means that the kernel notifies the process once it finds that one or more IO conditions specified by the process are ready to be read. IO multiplexing is applicable to the following scenarios:
(1) When the customer processes multiple descriptive words (generally interactive input and Network Interface), I/O must be reused.
(2) When a customer processes multiple sets of interfaces at the same time, this situation is possible, but rarely occurs.
(3) If a TCP server needs to process both the listener interface and the connected interface, I/O multiplexing is also required.
(4) If a server needs to process TCP and UDP, I/O multiplexing is generally required.
(5) If a server needs to process multiple services or protocols, I/O reuse is generally required.
Compared with multi-process and multi-thread technologies, the biggest advantage of I/O multiplexing technology is that the system overhead is small, and the system does not need to create processes/threads or maintain these processes/threads, this greatly reduces the system overhead.
2. select Function
This function allows the process to instruct the kernel to wait for any of the multiple events to be sent, and to wake up only when one or more events occur or after a specified period of time. The function prototype is as follows:
# Include
# Include
Int select (int maxfdp1, fd_set * readset, fd_set * writeset, fd_set * reset tset, const struct timeval * timeout) Return Value: Number of ready descriptors, 0 returned for timeout, error returned-1
Function parameters are described as follows:
(1) The first parameter maxfdp1 specifies the number of descriptive words to be tested. Its value is the maximum descriptive word to be tested plus 1 (therefore, this parameter is named maxfdp1 ), description 0, 1, 2... all maxfdp1-1 will be tested.
(2) the three parameters readset, writeset, and effectset in the middle specify the description words for the kernel test read, write, and exception conditions. If you are not interested in a condition, you can set it as a null pointer. Struct fd_set can be understood as a collection, which stores file descriptors and can be set through the following four macros:
Void FD_ZERO (fd_set * fdset); // clear the set
Void FD_SET (int fd, fd_set * fdset); // Add a given file descriptor to the set.
Void FD_CLR (int fd, fd_set * fdset); // delete a given file descriptor from the set
Int FD_ISSET (int fd, fd_set * fdset); // check whether the specified file descriptor in the set can be read and written.
(3) timeout indicates how much time the kernel can spend waiting for any of the specified descriptions. The timeval structure is used to specify the number of seconds and the number of microseconds during this period.
Struct timeval {
Long TV _sec; // seconds
Long TV _usec; // microseconds
};
There are three possibilities for this parameter:
(1) always wait: only when one descriptive word is ready for I/O. Therefore, set this parameter to NULL.
(2) Wait for a fixed period of time: returns the result when an I/O is prepared for a descriptive word, but does not exceed the number of seconds and microseconds specified in the timeval structure pointed by this parameter.
(3) Don't wait at all: Check the description and return immediately. This is called polling. Therefore, this parameter must point to a timeval structure, and the timer value must be 0.
3. Test Procedure
Write a TCP Echo program. The function of the program is: the client sends information to the server, the server receives and sends it to the client as is, and the client displays the received information.
The server program is as follows:
1 # include
2 # include
3 # include
4 # include
5 6 # include
7 # include
8 # include
9 # include
10 # include
11 12 # define IPADDRESS "127.0.0.1" 13 # define PORT 8787 14 # define MAXLINE 1024 15 # define LISTENQ 5 16 17 // function declaration 18 // create a socket and bind 19 static int socket_bind (const char * ip, int port); 20 // IO multiplexing select 21 static void do_select (int listenfd); 22 // process multiple connections 23 static void handle_connection (int * connfds, int num, fd_set * prset, fd_set * pallset); 24 25 int main (int argc, char * argv []) 26 {27 int listenfd, connfd, sockfd; 28 struct sockaddr_in cliaddr; 29 socklen_t cliaddrlen; 30 listenfd = socket_bind (IPADDRESS, PORT); 31 listen (listenfd, LISTENQ); 32 do_select (listenfd); 33 return 0; 34} 35 36 static int socket_bind (const char * ip, int port) 37 {38 int listenfd; 39 struct sockaddr_in servaddr; 40 listenfd = socket (AF_INET, SOCK_STREAM, 0 ); 41 if (listenfd =-1) 42 {43 perror ("socket error:"); 44 exit (1); 45} 46 bzero (& servaddr, sizeof (servaddr); 47 servaddr. sin_family = AF_INET; 48 inet_ton (AF_INET, ip, & servaddr. sin_addr); 49 servaddr. sin_port = htons (port); 50 if (bind (listenfd, (struct sockaddr *) & servaddr, sizeof (servaddr) =-1) 51 {52 perror ("bind error:"); 53 exit (1); 54} 55 return listenfd; 56} 57 58 static void do_select (int listenfd) 59 {60 int connfd, sockfd; 61 struct sockaddr_in cliaddr; 62 socklen_t cliaddrlen; 63 fd_set rset, allset; 64 int maxfd, maxi; 65 int I; 66 int clientfds [FD_SETSIZE]; // Save the client connection descriptor 67 int nready; 68 // initialize the client connection descriptor 69 for (I = 0; I <FD_SETSIZE; I ++) 70 clientfds [I] =-1; 71 maxi =-1; 72 FD_ZERO (& allset); 73 // Add the listening descriptor 74 FD_SET (listenfd, & allset ); 75 maxfd = listenfd; 76 // cyclic processing 77 for (;) 78 {79 rset = allset; 80 // obtain the number of available descriptors 81 nready = select (maxfd + 1, & rset, NULL); 82 if (nready =-1) 83 {84 perror ("select error:"); 85 exit (1 ); 86} 87 // test whether the listener descriptor is 88 if (FD_ISSET (listenfd, & rset) 89 {90 cliaddrlen = sizeof (cliaddr ); 91 // accept new connection 92 if (connfd = accept (listenfd, (struct sockaddr *) & cliaddr, & cliaddrlen) =-1) 93 {94 if (errno = EINTR) 95 continue; 96 else 97 {98 perror ("accept error:"); 99 exit (1 ); 100} 101} 102 fprintf (stdout, "accept a new client: % s: % d \ n", inet_ntoa (cliaddr. sin_addr), cliaddr. sin_port); 103 // Add the new connection descriptor to the array 104 for (I = 0; I
Maxfd? Connfd: maxfd); 121 // record the number of customer connection sockets 122 maxi = (I> maxi? I: maxi); 123 if (-- nready <= 0) 124 continue; 125} 126 // process client connection 127 handle_connection (clientfds, maxi, & rset, & allset ); 128} 129} 130 131 static void handle_connection (int * connfds, int num, fd_set * prset, fd_set * pallset) 132 {133 int I, n; 134 char buf [MAXLINE]; 135 memset (buf, 0, MAXLINE); 136 for (I = 0; I <= num; I ++) 137 {138 if (connfds [I] <0) 139 continue; 140 // test whether the customer descriptor is prepared 141 if (FD_ISSET (connfds [I], prset )) 142 {143 // the message sent by the receiving client 144 n = read (connfds [I], buf, MAXLINE); 145 if (n = 0) 146 {147 close (connfds [I]); 148 FD_CLR (connfds [I], pallset); 149 connfds [I] =-1; 150 continue; 151} 152 printf ("read msg is:"); 153 write (STDOUT_FILENO, buf, n); 154 // send buf155 write (connfds [I], buf, n); 156} 157} 158}
The client program is as follows:
1 # include
2 # include
3 # include
4 # include
5 # include
6 # include
7 # include
8 # include
9 # include
10 11 # define MAXLINE 102412 # define IPADDRESS "127.0.0.1" 13 # define SERV_PORT 878714 15 # define max (a, B) (a> B )? A: F8 17 static void handle_connection (int sockfd); 18 19 int main (int argc, char * argv []) 20 {21 int sockfd; 22 struct sockaddr_in servaddr; 23 sockfd = socket (AF_INET, SOCK_STREAM, 0); 24 bzero (& servaddr, sizeof (servaddr); 25 servaddr. sin_family = AF_INET; 26 servaddr. sin_port = htons (SERV_PORT); 27 inet_ton (AF_INET, IPADDRESS, & servaddr. sin_addr); 28 connect (sockfd, (struct sockaddr *) & servaddr, sizeof (servaddr); 29 // process the connection descriptor 30 handle_connection (sockfd); 31 return 0; 32} 33 34 static void handle_connection (int sockfd) 35 {36 char sendline [MAXLINE], recvline [MAXLINE]; 37 int maxfdp, stdineof; 38 fd_set rset; 39 int n; 40 FD_ZERO (& rset); 41 for (;) 42 {43 // Add standard input descriptor 44 FD_SET (STDIN_FILENO, & rset ); 45 // Add the connection descriptor 46 FD_SET (sockfd, & rset); 47 maxfdp = max (STDIN_FILENO, sockfd); 48 // round robin 49 select (maxfdp + 1, & rset, NULL, NULL, NULL); 50 // test whether the connection socket has 51 if (FD_ISSET (sockfd, & rset) 52 {53 n = read (sockfd, recvline, MAXLINE ); 54 if (n = 0) 55 {56 fprintf (stderr, "client: server is closed. \ n "); 57 close (sockfd); 58 FD_CLR (sockfd, & rset); 59} 60 write (STDOUT_FILENO, recvline, n ); 61} 62 // test whether the standard input is ready 63 if (FD_ISSET (STDIN_FILENO, & rset) 64 {65 n = read (STDIN_FILENO, sendline, MAXLINE ); 66 if (n = 0) 67 {68 FD_CLR (STDIN_FILENO, & rset); 69 continue; 70} 71 write (sockfd, sendline, n); 72} 73} 74}
4. Program Results
Start the service program and execute two customer programs for testing. The results are shown in: