poll()函数:
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; /* 要监听的文件描述符 */
short events; /* 监听的事件集合 */
short revents; /* 发生的事件集合 */
};
说明:poll()较select()函数有了进一步的优化,一方面,将监听事件集同返回事件集分开,避免了每次调用重复手动构建监听事件集的麻烦,另一方面,poll()没有监听文件描述符集最大数量的限制,其他功能类似select()函数。
参数:
- fds:是一个要监听的结构列表,每一个元素包括三个字段,fd:要监听的文件描述符,events:监听的事件集合,revents:发生的事件集合。
- nfds:fds列表的长度。
- timeout:poll()等待的时间,单位为ms,值为-1时与select的timeout参数值为NULL时功能相同,select()函数的timout参数。
返回值:
- 大于0:表示监听的文件描述符列表中,发生事件的文件描述符个数。
- 等于0:timeout时间内还没有事件发生,超时返回0。
- 小于-1:调用失败,错误原因存于errno。
备注:events和revents的取值范围如下所示:
事件 | 描述 | 是否可作为输入 | 是否可作为输出 |
---|---|---|---|
POLLIN | 数据(包括普通数据和优先数据)可读 | 是 | 是 |
POLLRDNORM | 普通数据可读 | 是 | 是 |
POLLPRI | 高优先级数据可读,如TCP带外数据 | 是 | 是 |
POLLOUT | 数据(包括普通数据和优先数据)可写 | 是 | 是 |
POLLWRNORM | 普通数据可写 | 是 | 是 |
POLLRDHUP | TCP连接被对方关闭,或者对方关闭了写操作 | 是 | 是 |
POLLERR | 错误 | 否 | 是 |
POLLHUP | 挂起,如管道的写端关闭后,读端将收到POLLHUP事件 | 否 | 是 |
POLLNVAL | 文件描述符没有打开 | 否 | 是 |
注:socket的就绪条件同select。
poll()优点:
- 将监听事件集同返回事件集分开,避免了每次调用重复手动构建监听事件集的麻烦。
- poll()没有监听文件描述符集最大数量的限制。
poll()缺点:
- 每次调用都需要把大量的poofd结构从用户拷贝到内核态。
- 内核在监听文件描述符集的时候需要轮询文件描述符列表,在文件描述符列表很大时,效率会很低。
- poll返回后,需要遍历pollfd列表获取就绪的文件描述符,在列表很大时,性能会下降。
基于poll()的ECHO服务器:
server.c:
#include<stdio.h>
#include<unistd.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<stdlib.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<poll.h>
void Init(struct pollfd* fdlist,int size){
int i = 0;
for(;i < size;i++){
fdlist[i].fd = -1;
fdlist[i].events = 0;
fdlist[i].revents = 0;
}
}
void Add(int fd,struct pollfd* fdlist,int size){
int i = 0;
for(;i < size;i++){
if(fdlist[i].fd == -1){
fdlist[i].fd = fd;
fdlist[i].events = POLLIN;
break;
}
}
}
int main(int argc,char *argv[]){
if(argc != 3){
printf("Usage :./server ip port\n");
return -1;
}
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0){
perror("socket");
return -2;
}
struct sockaddr_in saddr;
saddr.sin_family = AF_INET;
saddr.sin_addr.s_addr = inet_addr(argv[1]);
saddr.sin_port = htons(atoi(argv[2]));
int opt = 1;
setsockopt(sock,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));
int ret = bind(sock,(struct sockaddr*)&saddr,sizeof(saddr));
if(ret < 0){
perror("bind");
return -3;
}
ret = listen(sock,5);
if(ret < 0){
perror("listen");
return -4;
}
struct pollfd fdlist[1024];
Init(fdlist,sizeof(fdlist)/sizeof(struct pollfd));
Add(sock,fdlist,sizeof(fdlist)/sizeof(struct pollfd));
while(1){
int ret = poll(fdlist,sizeof(fdlist)/sizeof(struct pollfd),1000);
if(ret < 0){
perror("poll");
continue;
}
if(ret == 0){
printf("poll timeout\n");
continue;
}
int i = 0;
for(;i < sizeof(fdlist)/sizeof(struct pollfd); i++){
if(fdlist[i].fd == -1){
continue;
}
if(!(fdlist[i].revents & POLLIN)){
continue;
}
if(fdlist[i].fd == sock){
struct sockaddr_in caddr;
socklen_t len = sizeof(caddr);
int connect_fd = accept(sock,(struct sockaddr*)&caddr,&len);
if(connect_fd < 0){
perror("connect");
continue;
}
Add(connect_fd,fdlist,sizeof(fdlist)/sizeof(struct pollfd));
}else{
char buf[1024] = {0};
ssize_t readsize = read(fdlist[i].fd,buf,sizeof(buf)-1);
if(readsize < 0){
perror("read");
continue;
}
if(readsize == 0){
printf("client say goodbay\n");
close(fdlist[i].fd);
fdlist[i].fd = -1;
fdlist[i].events = 0;
fdlist[i].revents = 0;
continue;
}
printf("client $:%s",buf);
write(fdlist[i].fd,buf,strlen(buf));
}
}
}
return 0;
}
client.c:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<string.h>
int main(int argc,char *argv[]){
if(argc != 3){
perror("Usage :./server ip port\n");
return -1;
}
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(argv[1]);
addr.sin_port = htons(atoi(argv[2]));
int sock = socket(AF_INET,SOCK_STREAM,0);
if(sock < 0){
perror("socket");
return -1;
}
int ret = connect(sock,(struct sockaddr*)&addr,sizeof(addr));
if(ret < 0){
perror("connect");
return -2;
}
while(1){
char buf[1024] = {0};
printf("> ");
fflush(stdout);
read(0,buf,sizeof(buf)-1);
ssize_t writesize = write(sock,buf,strlen(buf));
if(writesize < 0){
perror("write");
continue;
}
ssize_t readsize = read(sock,buf,sizeof(buf)-1);
if(readsize < 0){
perror("read");
continue;
}
if(readsize == 0){
printf("server close\n");
break;
}
buf[readsize] = 0;
printf("server say:%s",buf);
}
close(sock);
return 0;
}
结果展示: