为了技术,我不会停下学习的脚步,我相信我还能走二十年。
分类:
2012-06-29 16:56:56
原文地址:poll函数 作者:jianhuali0118
poll函数起源于SVR3,最初局限于流设备。SVR4取消了这种限制,允许poll工作在任何描述字上。poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息。
1.
#include
2.
3.
int
poll(
struct
pollfd *fdarray, unsigned
long
nfds,
int
timeout);
4.
返回:就绪描述字的个数,0-超时,-1-出错
第一个参数是指向一个结构数组第一个元素的指针。每个数组元素都是一个pollfd结构,用于指定测试某个给定描述字fd的条件。
struct pollfd{
int fd; //descriptor to check
short events; //events of interest on fd
short revents; //events that occurred on fd
};
要测试的条件由events成员指定,而返回的结果则在revents中存储。常用条件及含意说明如下:
常量 | 说明 |
POLLIN | 普通或优先级带数据可读 |
POLLRDNORM | 普通数据可读 |
POLLRDBAND | 优先级带数据可读 |
POLLPRI | 高优先级数据可读 |
POLLOUT | 普通数据可写 |
POLLWRNORM | 普通数据可写 |
POLLWRBAND | 优先级带数据可写 |
POLLERR | 发生错误 |
POLLHUP | 发生挂起 |
POLLNVAL | 描述字不是一个打开的文件 |
注意:后三个只能作为描述字的返回结果存储在revents中,而不能作为测试条件用于events中。
第二个参数nfds是用来指定数组fdarray的长度。
最后一个参数timeout是指定poll函数返回前等待多长时间。它的取值如下:
timeout值 | 说明 |
INFTIM | 永远等待 |
0 | 立即返回,不阻塞进程 |
>0 | 等待指定数目的毫秒数 |
一个使用poll的网络程序例子:
001.
/**
002.
*TCP回射服务器的服务端程序
003.
*/
004.
#include
005.
#include
006.
#include
007.
#include
008.
#include
009.
#include
010.
#include
011.
#include
012.
#include
013.
#include //for poll
014.
015.
#define LISTENQ 1024
016.
#define MAXLINE 1024
017.
#define OPEN_MAX 50000
018.
#define SERVER_PORT 3333
019.
020.
#ifndef INFTIM /*按照书上解释:POSIX规范要求INFTIM在头文件中定义,不过*/
021.
#define INFTIM -1 /*许多系统仍然把它定义在头文件中,但是经过我的测试*/
022.
#endif /*即使都包含这两个文件,编译器也找不到,不知何解。索性自己定义了。*/
023.
024.
int
main(
int
argc,
char
*argv[])
025.
{
026.
int
i, maxi, listenfd, connfd, sockfd;
027.
int
nready;
028.
ssize_t n;
029.
socklen_t clilen;
030.
struct
sockaddr_in servaddr, cliaddr;
031.
struct
hostent *hp;
032.
char
buf[BUFSIZ];
033.
struct
pollfd client[OPEN_MAX];
/*用于poll函数第一个参数的数组*/
034.
035.
if
( argc != 2 )
036.
{
037.
printf
(
"Please input %s \n"
, argv[0]);
038.
exit
(1);
039.
}
040.
041.
//创建socket
042.
if
( (listenfd = socket(AF_INET, SOCK_STREAM,0)) < 0 )
043.
{
044.
printf
(
"Create socket error!\n"
);
045.
exit
(1);
046.
}
047.
048.
//设置服务器地址结构
049.
bzero(&servaddr,
sizeof
(servaddr));
050.
servaddr.sin_family = AF_INET;
051.
if
( (hp = gethostbyname(argv[1])) != NULL )
052.
{
053.
bcopy(hp->h_addr, (
struct
sockaddr*)&servaddr.sin_addr, hp->h_length);
054.
}
055.
else
if
(inet_aton(argv[1], &servaddr.sin_addr) < 0 )
056.
{
057.
printf
(
"Input Server IP error!\n"
);
058.
exit
(1);
059.
}
060.
servaddr.sin_port = htons(SERVER_PORT);
061.
062.
//绑定地址
063.
if
( bind(listenfd, (
struct
sockaddr*)&servaddr,
sizeof
(servaddr)) < 0 )
064.
{
065.
printf
(
"IPaddress bound failure!\n"
);
066.
exit
(1);
067.
}
068.
069.
//开始监听
070.
listen(listenfd, LISTENQ);
071.
072.
client[0].fd = listenfd;
/*将数组中的第一个元素设置成监听描述字*/
073.
074.
client[0].events = POLLIN;
/*将测试条件设置成普通或优先级带数据可读,此处书中为POLLRDNORM,
075.
但是怎么也编译不过去 ,编译器就是找不到,所以就临时改成了POLLIN这个条件,
076.
希望以后能弄清楚。
077.
*/
078.
079.
for
(i = 1;i < OPEN_MAX; ++i)
/*数组中的其它元素将暂时设置成不可用*/
080.
client[i].fd = -1;
081.
maxi = 0;
082.
083.
while
(1)
084.
{
085.
nready = poll(client, maxi+1,INFTIM);
//将进程阻塞在poll上
086.
if
( client[0].revents & POLLIN
/*POLLRDNORM*/
)
/*先测试监听描述字*/
087.
{
088.
connfd = accept(listenfd,(
struct
sockaddr*)&servaddr, &clilen);
089.
for
(i = 1; i < OPEN_MAX; ++i)
090.
if
( client[i].fd < 0 )
091.
{
092.
client[i].fd = connfd;
/*将新连接加入到测试数组中*/
093.
client[i].events = POLLIN;
//POLLRDNORM; /*测试条件普通数据可读*/
094.
break
;
095.
}
096.
if
( i == OPEN_MAX )
097.
{
098.
printf
(
"too many clients"
);
//连接的客户端太多了,都达到最大值了
099.
exit
(1);
100.
}
101.
102.
if
( i > maxi )
103.
maxi = i;
//maxi记录的是数组元素的个数
104.
105.
if
( --nready <= 0 )
106.
continue
;
//如果没有可读的描述符了,就重新监听连接
107.
}
108.
109.
for
(i = 1; i <= maxi; i++)
/*测试除监听描述字以后的其它连接描述字*/
110.
{
111.
if
( (sockfd = client[i].fd) < 0)
/*如果当前描述字不可用,就测试下一个*/
112.
continue
;
113.
114.
if
(client[i].revents & (POLLIN
/*POLLRDNORM*/
| POLLERR))
/*如果当前描述字返回的是普通数据可读或出错条件*/
115.
{
116.
if
( (n = read(sockfd, buf, MAXLINE)) < 0)
//从套接口中读数据
117.
{
118.
if
(
errno
== ECONNRESET)
//如果连接断开,就关闭连接,并设当前描述符不可用
119.
{
120.
close(sockfd);
121.
client[i].fd = -1;
122.
}
123.
else
124.
perror
(
"read error"
);
125.
}
126.
else
if
(n == 0)
//如果数据读取完毕,关闭连接,设置当前描述符不可用
127.
{
128.
close(sockfd);
129.
client[i].fd = -1;
130.
}
131.
else
132.
write(sockfd, buf, n);
//打印数据
133.
134.
if
(--nready <= 0)
135.
break
;
136.
137.
}
138.
}
139.
}
140.
141.
exit
(0);
142.
}
注:本章内容摘自<Unix 网络编程>第六章。