POLL函数概念
Poll函数和select类似,但它是用文件描述符而不是条件的类型来组织信息的. 也就是说,一个文件描述符的可能事件都存储在struct pollfd中.与之相反,select用事件的类型来组织信息,而且读,写和错误情况都有独立的描述符掩码.poll函数是POSIX:XSI扩展的一部分,它起源于UNIX System V
函数poll原型
包含头文件<poll.h> 功能:与select函数功能相同 原型: int poll(struct pollfd *fdarray,unsigned long nfds,int timeout); 参数 fdarray是一个pollfd的机构体数组用来表示表示文件描述符的监视信息 nfds参数给出了要监视的描述符数目 timeout参数是一个用豪秒表示的时间,是poll在返回前没有接收事件是应等待的时间,如果timeout的值为-1,poll就永远不会超时.如果整数值为32个比特,那么最大超时周期约为30分钟 返回值:准备好描述字的个数,0-超时,1-出错
Pollfd结构体
fd是文件描述符值 event和revents是通过代表各种事件的标准符进行逻辑或运算构建而成的
Struct pollfd {
int fd;
short events; //感兴趣的事件
short revents; //fd上触发的事情
}
Poll函数事件标志
事件标志符 | 含义 |
POLLIN | 无阻塞地读除了具有高优先级的数据之外的数据 |
POLLRONORM | 无阻塞地读常规数据 |
POLLRDBAND | 无阻塞地读具有优先级的数据 |
POLLOUT | 无阻塞的写常规数据 |
Poll函数实现
与之前的select实现基本一致,实现c/s架构
首先服务器:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h> /* for struct sockaddr_in*/
#include <sys/errno.h>
#include <signal.h>
#include <sys/select.h>
#include <poll.h>
//关于IO复用服务器的poll
#define LISTEN_SIZE 1024
void error_exit(char *name)
{
perror(name);
exit(-1);
}
int main(int argc,char *argv[])
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
error_exit("create error");
}
//绑定地址(ip和端口号)
struct sockaddr_in svraddr;
memset(&svraddr,0,sizeof(svraddr));
svraddr.sin_family=AF_INET;
svraddr.sin_addr.s_addr=INADDR_ANY;
//svraddr.sin_addr.s_addr=inet_addr("127.0.0.1");第二种写法
svraddr.sin_port=htons(5555);
int ret=bind(sockfd,(struct sockaddr*)&svraddr,sizeof(svraddr));
if(ret<0)
{
error_exit("bind error");
}
//设置监听参数back login 半连接数最大
ret=listen(sockfd,1024);
if(ret<0)
{
error_exit("listen error");
}
//创建poll结构体
struct pollfd pollfds[LISTEN_SIZE]={0};
int i=0;
for(;i<LISTEN_SIZE;i++)
{
pollfds[i].fd=-1;
}
pollfds[0].fd=sockfd;
pollfds[0].events=POLLIN;
struct sockaddr_in removeaddr;
int addr_len=sizeof(removeaddr);
char *buf[1024]={0};
while(1)
{
int nevent=poll(pollfds,LISTEN_SIZE,-1);
if(nevent==0)
{
printf("timeout\n");
continue;
}
else if(nevent<0)
{
error_exit("poll error");
}
if(pollfds[0].revents&POLLIN)
{
int fd=accept(sockfd,(struct sockaddr *)&removeaddr,&addr_len);
if(fd<0)
{
error_exit("accept error");
}
for(i=1;i<LISTEN_SIZE;i++)
{
if(pollfds[i].fd==-1)
{
pollfds[i].fd=fd;
pollfds[i].events=POLLIN;
break;
}
}
for(i=1;i<LISTEN_SIZE;i++)
{
if(pollfds[i].fd==-1)
{
continue;
}
//判断是否为可读事件
if(pollfds[i].revents&POLLIN)
{
int rdsize=read(pollfds[i].fd,buf,1024);
if(rdsize<=0)
{
printf("close %d\n",pollfds[i].fd);
close(pollfds[i].fd);
pollfds[i].fd=-1;
}
else
{
printf("read buf =%s,fd=%d\n",buf,pollfds[i].fd);
}
}
}
}
}
}
客户端:
使用之前的代码,基本不变,贴上来
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <netinet/in.h> /* for struct sockaddr_in*/
#include <sys/errno.h>
#include <signal.h>
//关于客户端的socket
void error_exit(char *name)
{
perror(name);
exit(-1);
}
int main(int argc,char *argv[])
{
if(argc<3)
{
printf("run program+ip+port\n");
return-1;
}
int sockfd=socket(AF_INET,SOCK_STREAM,0);
if(sockfd<0)
{
error_exit("create error");
}
//连接服务器,设置服务器的地址(ip和端口)
struct sockaddr_in svraddr;
memset(&svraddr,0,sizeof(svraddr));
svraddr.sin_family=AF_INET;
svraddr.sin_addr.s_addr= inet_addr(argv[1]);
svraddr.sin_port=htons(atoi(argv[2]));
int ret =connect(sockfd,(struct sockaddr *)&svraddr,sizeof(svraddr));
if(ret<0)
{
error_exit("connect error");
}
write(sockfd,"hello",strlen("hello"));
sleep(5);
close(sockfd);
return 0;
}
结果:
客户端执行后台操作
服务器接受到的结果
总结:相比较之前的select函数,poll没有FD_SETSIZE这个限制,poll的出现可以解决select的fd数量限制