I/O multiplexing of network programming under Unix (2)

select function

This function allows a process to instruct the kernel to wait for any one of several events to occur, and wake it up only after one or more events have occurred or a specified period of time has elapsed. We call select to tell the kernel which descriptors (read, write, or exception conditions) it is interested in and how long to wait. The descriptors we are interested in are not limited to sockets, any descriptor can be tested using select.

select function prototype:

#include<sys/select.h> 
#include<sys/time.h>
int select (int maxfd , fd_set *readset ,fd_set *writeset, fd_set *exceptionset , const struct timeval *timeout);
return: positive of the ready descriptor number, 0 - timeout, -1 - error

The parameters of the select function are introduced: maxfd represents the number of description words to be tested , and its value should be the largest description word + 1. The middle readset, writeset, exceptionset specifies the description words that we want the kernel to test read, write, and exception conditions. The last parameter tells the kernel how long to wait for any of the specified descriptors to become ready.

timeval structure:

struct timeval {
long tv_sec; //seconds
long tv_usec ; //microseconds
}

The timeval parameter has three possible values: 1. NULL: means to wait forever, which is equivalent to complete blocking. 2. A fixed value, representing waiting for a fixed period of time. 3. The attribute value of timeval is 0, which means that it does not wait at all, and returns immediately after checking the description word, that is to say, it is non-blocking.

fd_set structure:

The fd_set structure represents a description word set. It should typically be represented as an array of integers, where each bit in each integer corresponds to a descriptor. There are the following four macros about fd_set:

void FD_ZERO(fd_set *fdset); /* clear all bits in fdset */
void FD_SET(int fd, fd_set *fdset); /* turn on the bit for fd in fdset */
void FD_CLR(int fd, fd_set *fdset); /* turn off the bit for fd in fdset */
int FD_ISSET(int fd, fd_set *fdset); /* is the bit for fd on in fdset ? */

The select function modifies the descriptor set pointed to by the pointers readset, writeset, and exceptionset, so these three parameters are all value-result parameters. That is to say, during the execution of the select function, the value in it will be modified. When calling this function, we specify the value of the descriptor we care about, and when the function returns, the result indicates which descriptors are ready. After the function returns, we use FD_ISSET to test the description word in the fd_set data type. Any bits in the descriptor set corresponding to the descriptors that are not ready are cleared to 0 when they are returned. For this reason, every time the select function is called again, we have to set the concerned positions in all descriptor sets to 1 again. This is also the reason why we set up two sets of resset and allset in the communication example later on.

The select function returns the condition that a socket is ready:

Communication example of select function: a simple TCP echo server program

SelectServer.c: Server-side program using select mechanism

 

copy code
  1 #include <stdio.h> 
2 #include <string.h>
3 #include <arpa/inet.h>
4 #include <netinet/in.h>
5 #include <sys/socket.h>
6 #include < sys/select.h>
7
8 const static int MAXLINE = 1024;
9 const static int SERV_PORT = 10001;
10
11 int main1()
12 {
13 int i , maxi , maxfd, listenfd , connfd , sockfd ;
14 /*nready description The number of words*/
15 int nready ,client[FD_SETSIZE];
16 int n ;
17 /*Create a description word set, because the select function will clear the description words that have not occurred, so we set two sets*/
18 fd_set rset , allset;
19 char buf[MAXLINE];
20 socklen_t clilen;
21 struct sockaddr_in cliaddr , servaddr;
22 /*Create socket*/
23 listenfd = socket(AF_INET , SOCK_STREAM , 0);
24 /*Define sockaddr_in*/
25 memset(&servaddr , 0 ,sizeof(servaddr));
26 servaddr.sin_family = AF_INET;
27 servaddr.sin_port = htons(SERV_PORT);
28 servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
29
30 bind(listenfd, (struct sockaddr *) & servaddr , sizeof(servaddr));
31 listen(listenfd , 100);
32 /*listenfd is the first descriptor */
33 /* the largest descriptor used for the first parameter of the select function */
34 maxfd = listenfd;
35 /* the number of clients used for polling */
36 maxi = -1 ;
37 /*init*/
38 for(i=0 ;i<FD_SETSIZE ; i++)
39 client[i] = -1;
40 FD_ZERO(&allset);
41 FD_SET(listenfd, &allset);
42
43 for (;;)
44 {
45 rset = allset;
46 /*Only select the descriptor for reading, block without timeout*/
47 nready = select (maxfd+1 , &rset , NULL , NULL , NULL);
48 if(FD_ISSET(listenfd,&rset))
49 {
50 clilen = sizeof(cliaddr);
51 connfd = accept(listenfd , (struct sockaddr *) & cliaddr , &clilen );
52 /*Find the first position where the new descriptor can be placed*/
53 for (i=0;i<FD_SETSIZE;i++)
54 {
55 if(client[i]<0)
56 {
57 client[i ] = connfd;
58 break;
59 }
60 }
61 /*Cannot find, it means the client is full*/
62 if(i==FD_SETSIZE)
63 {
64 printf("Too many clients , over stack .\n");
65 return - 1;
66 }
67 FD_SET(connfd,&allset);//Set fd
68 /*Update related parameters*/
69 if(connfd > maxfd) maxfd = connfd;
70 if(i>maxi) maxi = i;
71 if(nready <=1) continue;
72 else nready --;
73 }
74
75 for(i=0 ; i<=maxi ; i++)
76 {
77 if (client[i]<0) continue;
78 sockfd = client[i];
79 if(FD_ISSET(sockfd,&rset))
80 {
81 n = read(sockfd , buf , MAXLINE);
82 if (n==0)
83 {
84 /*When the other party closes , the server closes the descriptor and clears the sockfd of the set */
85 close(sockfd);
86 FD_CLR(sockfd,&allset);
87 client[i] = -1;
88 }
89 else
90 {
91 buf[n]=' \0';
92 printf("Socket %d said : %s\n",sockfd,buf);
93 write(sockfd,buf,n); //Write back to client
94 }
95 nready --;
96 if(nready<=0) break;
97 }
98 }
99
100 }
101 return 0;
102 }
copy code

 

Client.c: simple client program

copy code
 1 #include <stdio.h>
2 #include <string.h>
3 #include <arpa/inet.h>
4 #include <netinet/in.h>
5 #include <sys/socket.h>
6
7 #define MAXLINE 1024
8 int main()
9 {
10 int sockfd ,n;
11 char buf [MAXLINE];
12 sockfd = socket(AF_INET,SOCK_STREAM ,0);
13 struct sockaddr_in servaddr;
14 memset(&servaddr, 0 ,sizeof(servaddr));
15 servaddr.sin_family = AF_INET;
16 servaddr.sin_port = htons(10001);
17 inet_pton( AF_INET ,"127.0.0.1" , &servaddr.sin_addr ) ;
18
19 connect(sockfd,(struct sockaddr *)&servaddr , sizeof(servaddr));
20 while(1)
21 {
22 printf("type some words ...\n");
23 scanf("%s",buf);
24 write(sockfd,buf,sizeof(buf));
25 n = read(sockfd,buf,MAXLINE);
26 printf("%d bytes received \n ",n);
27 buf[n] = '\0';
28 printf("%s\n",buf);
29 }
30 close(sockfd);
31 return 0;
32 }
copy code

 

Summarize:

In this article, the relevant content of the select function in I/O multiplexing is introduced, and an example of a typical TCP reflection program is given. Using select can handle the concurrent requirements of multiple clients. In the next article, we will focus on the use of another common I/O multiplexing function poll.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325038264&siteId=291194637