poll和select对比
- 不同:
select使用三个位图来表示三个fdset的方式,poll使用一个 pollfd的指针实现。
pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式。
pollfd并没有最大数量限制(但是数量过大后性能也是会下降)。 - 相同:
和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。
从上面看,select和poll都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。
poll函数介绍
#include <poll.h>
int poll ( struct pollfd * fds, unsigned int nfds, int timeout);
pollfd结构体定义如下:
struct pollfd {
int fd; /* 文件描述符 */
short events; /* 等待的事件 */
short revents; /* 实际发生了的事件 */
} ;
每一个pollfd结构体指定了一个被监视的文件描述符,可以传递多个结构体,指示poll()监视多个文件描述符。每个结构体的events域是监视该文件描述符的事件掩码,由用户来设置这个域。revents域是文件描述符的操作结果事件掩码,内核在调用返回时设置这个域。events域中请求的任何事件都可能在revents域中返回。常见的事件如下:
图片转自:https://blog.youkuaiyun.com/lianghe_work/article/details/46534029
返回值和错误代码
成功时,poll()返回结构体中revents域不为0的文件描述符个数;如果在超时前没有任何事件发生,poll()返回0;失败时,poll()返回-1,并设置errno为下列值之一:
EBADF 一个或多个结构体中指定的文件描述符无效。
EFAULTfds 指针指向的地址超出进程的地址空间。
EINTR 请求的事件之前产生一个信号,调用可以重新发起。
EINVALnfds参数超出PLIMIT_NOFILE值。
ENOMEM 可用内存不足,无法完成请求。
代码示例
代码转自:https://blog.youkuaiyun.com/gaodes/article/details/82823577
server:
#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);
}
}
}
}
}
}
client:
#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;
}