之前我们介绍select 的时候,我们已经说过了I/O复用的相关概念,话不多说,今天我们就直接来看I/O复用的另一个系统调用:poll。
poll系统调用和select相似,也是在指定的时间内轮询一定量的文件描述符,以测试其中是否有就绪事件。原型如下:
(1)fd参数是一个polled结构体类型的数组,原型如下:
event成员告知poll监听哪些事件,也就是用户感兴趣的事件,是一系列的按位或;而revents成员则是由内核修改的,通知程序fd上实际发生的事件,poll支持的事件可以参照下表:
可以看出poll将POLLIN事件和POLLOUT事件划分的更加详细了,以区别对待普通数据和优先数据,可惜Linux不完全支持。
之前select中我们判断客户端是否断开连接通常是用recv的返回值是否等于0来判断,事实上这样不是特别靠谱的,所以呢,对于断开连接的情况,GNU就为poll增加了一个POLLRDHUP事件,它负责在socket上接收对端关闭连接触发,但是这个事件触发必然会引起EPLLIN事件,一点一定要注意啊反正第一次我是忘了,否则在编写代码时有可能会出错很难办。另外,使用EPOLLRDHUP事件需要定义_GNU_SOURCE。
(2)nfds参数指定被监听事件集合fds的大小
(3)timeout参数指定poll的超时值,当timeout为-1时,poll调用永久阻塞,直到事件发生。
另外,poll调用的系统返回值含义与select相同。
poll的应用(代码实现)
#define _GNU_SOURCE//千万要记得啊
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
#include<assert.h>
#include<arpa/inet.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<sys/select.h>
#include<poll.h>
void Init(struct pollfd *fds)
{
int i=0;
for(;i<100;i++)
{
fds[i].fd=-1;
fds[i].events=0;
fds[i].revents=0;
}
}
void Insert(struct pollfd *fds,int fd,short event)
{
int i=0;
for(;i<100;i++)
{
if(fds[i].fd==-1)
{
fds[i].fd=fd;
fds[i].events=event;
break;
}
}
}
void Delete(struct pollfd *fds,int fd)
{
int i=0;
for(;i<100;i++)
{
if(fds[i].fd==fd)
{
fds[i].fd=-1;
fds[i].events=0;
break;
}
}
}
int main()
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
assert(sockfd!=-1);
struct sockaddr_in ser,cli;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(6000);
ser.sin_addr.s_addr=inet_addr("127.0.0.1");
int res=bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
assert(res !=-1);
listen(sockfd,5);
struct pollfd fds[100];
Init(fds);
Insert(fds,sockfd,POLLIN);
while(1)
{
int n=poll(fds,100,-1);
if(n<=0)
{
printf("erron\n");
continue;
}
int i=0;
for(;i<100;i++)
{
if(fds[i].fd!=-1)
{
int fd=fds[i].fd;
//先处理关闭连接事件,因为它也引起的POLLIN事件
if(fds[i].revents & POLLRDHUP)
{
close(fd);
Delete(fds,fd);
}
else if(fds[i].revents & POLLIN)
{
if(fd==sockfd)
{
int len =sizeof(cli);
int c=accept(fd,(struct sockaddr*)&cli,&len);
if(c<0)
{
continue;
}
Insert(fds,c,POLLIN|POLLRDHUP);
}
else
{
char buff[128]={0};
recv(fd,buff,127,0);
printf("%d:%s\n",fd,buff);
send(fd,"ok",2,0);
}
}
}
}
}
}