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

poll function

The relevant use of the select function has been introduced in the I/O multiplexing (2) of network programming under Unix     above . This article will introduce another commonly used I/O multiplexing function poll. The functionality provided by poll is similar to that of select, but it can provide additional information when dealing with streaming devices.

The prototype of the poll function:

1
2
3
#include<poll.h>
    int  poll ( struct  pollfd * fdarray , unsigned long  nfds , int  timeout);
    //返回:就需描述字的个数,0——超时,-1——出错

 The first argument is a pointer to the first element of an array of structures, each of which is a pollfd structure. as follows:

1
2
3
4
5
struct  pollfd {
     int  fd; //descriptor to check
     short  events; //events of interest on fd
`   short  revents; //events tha occurred on fd
}

The condition to be tested is specified by the events member, and the function returns the state of the descriptor in the corresponding revents idiom. (Each descriptor has two variables, one for the call value and one for the return result, thus avoiding the use of value-result parameters, which is different from the select function). The following figure lists some constants used to specify the events flag and test the revents flag.

image

It should be noted in the above figure that POLLERR, POLLHUP, and POLLNVAL are description words for handling errors, so they cannot appear in the input event, that is, events. Poll identifies three types of data: normal, priority band, and high priority.

For TCP and UPD, the following conditions cause poll to return specific revents.

1、 All regular TCP data and all UDP data is considered normal. 
2、 TCP's out-of-band data (Chapter 24) is considered priority band. 
3、 When the read half of a TCP connection is closed (e.g., a FIN is received), this is also considered normal data and a subsequent read operation will return 0. 
4、 The presence of an error for a TCP connection can be considered either normal data or an error (POLLERR). In either case, a subsequent read will return –1 with errno set to the appropriate value. This handles conditions such as the receipt of an RST or a timeout. 
5、 The availability of a new connection on a listening socket can be considered either normal data or priority data. Most implementations consider this normal data. 
6、 The completion of a nonblocking connect is considered to make a socket writable.

                                                                                    -- "unix network programming" third edition

The parameter nfds indicates the number of elements in the structure array.

Parameter timeout:

Different from the timeout in select, the timeout parameter of the poll function is an int value, indicating how long to wait before the poll function returns, which is in milliseconds. It has three values: 1. INFTIM (a negative value), which means waiting forever, that is, always blocking. 2, 0, which means return immediately, non-blocking. 3. >0, indicating that the specified number of milliseconds is waiting.

The return value of the poll function:

When an error occurs in poll, the return value of the poll function is -1. If there is no description word ready before the timer expires, it returns 0. Otherwise, it returns the number of ready description words, that is, the number of description words whose revents member value is not 0. number.

If we no longer care about a particular descriptor, we can set the fd member of the pollfd structure corresponding to it to a negative value. The poll function will ignore the events member of such a pollfd structure and return with the value of its reevents member set to 0.

 

The communication example of the poll function: a simple TCP echo server program

pollServer.c: server program using select mechanism

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#include <stdio.h>
#include <string.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <poll.h>
 
/*环境为ubuntu10.04自带c环境,无法自动引入下列宏,所以自己写在前面了*/
#define INFTIM -1
#define POLLRDNORM  0x040       /* Normal data may be read.  */
#define POLLRDBAND  0x080       /* Priority data may be read.  */
#define POLLWRNORM  0x100       /* Writing now will not block.  */
#define POLLWRBAND  0x200       /* Priority data may be written.  */
 
#define MAXLINE  1024
#define OPEN_MAX  16 //一些系统会定义这些宏
#define SERV_PORT  10001
 
int  main()
{
     int  i , maxi ,listenfd , connfd , sockfd ;
     int  nready;
     int  n;
     char  buf[MAXLINE];
     socklen_t clilen;
     struct  pollfd client[OPEN_MAX];
 
     struct  sockaddr_in cliaddr , servaddr;
     listenfd = socket(AF_INET , SOCK_STREAM , 0);
     memset (&servaddr,0, sizeof (servaddr));
     servaddr.sin_family = AF_INET;
     servaddr.sin_port = htons(SERV_PORT);
     servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
 
     bind(listenfd , ( struct  sockaddr *) & servaddr, sizeof (servaddr));
     listen(listenfd,10);
     client[0].fd = listenfd;
     client[0].events = POLLRDNORM;
     for (i=1;i<OPEN_MAX;i++)
     {
         client[i].fd = -1;
     }
     maxi = 0;
 
     for (;;)
     {
         nready = poll(client,maxi+1,INFTIM);
         if  (client[0].revents & POLLRDNORM)
         {
             clilen = sizeof (cliaddr);
             connfd = accept(listenfd , ( struct  sockaddr *)&cliaddr, &clilen);
             for (i=1;i<OPEN_MAX;i++)
             {
                 if (client[i].fd<0)
                 {
                     client[i].fd = connfd;
                     client[i].events = POLLRDNORM;
                     break ;
                 }
             }
             if (i==OPEN_MAX)
             {
                 printf ( "too many clients! \n" );
             }
             if (i>maxi) maxi = i;
             nready--;
             if (nready<=0) continue ;
         }
 
         for (i=1;i<=maxi;i++)
         {
             if (client[i].fd<0) continue ;
             sockfd = client[i].fd;
             if (client[i].revents & (POLLRDNORM|POLLERR))
             {
                 n = read(client[i].fd,buf,MAXLINE);
                 if (n<=0)
                 {
                     close(client[i].fd);
                     client[i].fd = -1;
                 }
                 else
                 {
                     buf[n]= '\0' ;
                     printf ( "Socket %d said : %s\n" ,sockfd,buf);
                     write(sockfd,buf,n); //Write back to client
                 }
                 nready--;
                 if (nready<=0) break ; //no more readable descriptors
             }
         }
     }
     return  0;
}

For the client program, refer to the previous article.

Summarize:

   This article introduces the prototype of the poll function, parameter descriptions, precautions and a simple code example. In subsequent versions of Unix, the epoll function I/O multiplexing mechanism has been added, which is more efficient under certain conditions. In future articles, the epoll mechanism will be described in detail. When I was learning python before, I also came into contact with select and poll, but the understanding was relatively simple at that time. I hope that through the recent study, I can have a deeper understanding of I/O multiplexing under Unix.

Guess you like

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