poll函數html
在上文unix下網絡編程之I/O複用(二)中已經介紹了select函數的相關使用,本文將介紹另外一個經常使用的I/O複用函數poll。poll提供的功能與select相似,不過在處理流設備時,它可以提供額外的信息。python
poll函數原型:編程
1
2
3
|
#include<poll.h>
int
poll (
struct
pollfd * fdarray , unsigned
long
nfds ,
int
timeout);
//返回:就需描述字的個數,0——超時,-1——出錯
|
第一個參數是指向一個結構數組第一個元素的指針,每一個數組元素都是一個pollfd結構。以下:ubuntu
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
}
|
要測試的條件由events成員指定,函數在相應的revents成語中返回該描述字的狀態。(每一個描述字都有兩個變量,一個爲調用值,另外一個爲返回結果,從而避免使用值-結果參數,這與select函數是不一樣的)。下圖列出了用於指定events標誌以及測試revents標誌的一些常值。數組
上圖須要注意的是,POLLERR,POLLHUP,POLLNVAL是處理錯誤的描述字,所以它們也就不能夠出如今input事件中,即events。poll識別三類數據:普通(normal),優先級帶(priority band)和高優先級(high priority)。服務器
對TCP和UPD而言,如下條件引發poll返回特定的revents。網絡
一、 All regular TCP data and all UDP data is considered normal.
二、 TCP's out-of-band data (Chapter 24) is considered priority band.
三、 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.
四、 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.
五、 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.
六、 The completion of a nonblocking connect is considered to make a socket writable.app
——《unix網絡編程》第三版socket
參數nfds,指示結構數組中元素的個數。ide
參數timeout:
與select中的timeout不一樣,poll函數的timeout參數是一int值,表示poll函數返回前等待多長時間,它是毫秒級別的。它有三種狀況的取值:
一、INFTIM(一個負數值 -1),表示永遠等待,即一直阻塞,-1:一直阻塞到fds數組中有一個達到就緒態或者捕獲到一個信號。
二、0,表示當即返回,非阻塞。
三、>0,表示正待指定數目的毫秒數。
poll函數的返回值:
當poll發生錯誤時,poll函數的返回值-1,若定時器時間到以前沒有任何描述字就緒,則返回0,不然返回就緒描述字的個數,即其revents成員值非0的描述字個數。
若是咱們再也不關心某個特定描述字,那麼能夠把與他對應的pollfd結構的fd成員設置成一個負值。poll函數將忽略這樣的pollfd結構的events成員,返回時將它的revents成員的值置爲0。
poll函數的通訊列子:一個簡單的TCP回射服務器程序
pollServer.c:使用select機制的服務器程序
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;
}
|
客戶端程序參考上一篇文章。
總結:
本文介紹了poll函數的原型,參數說明,注意事項以及一個簡單的代碼例子。在unix後續版本中,加入了epoll函數I/O複用機制,它在必定條件下更加高效,在之後的文章中,會對epoll機制再進行詳細的描述。以前在學習python的時候,也接觸了select和poll,可是當時瞭解的比較淺顯,但願經過最近的學習能夠對unix下I/O複用有更深刻的認識。