poll的机制与select类似,与select在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll没有最大文件描述符数量的限制。poll和select同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
一.poll()函数
#include<poll.h>
int poll(struct pollfd* fds,nfds_t fds,int timeout);
第一个参数是一个pollfd类型的结构体,里边有文件描述符,注册的事件和实际发生的事件。
pollfd结构体的定义如下:
struct pollfd
{
int fd;//文件描述符
short envents;//注册的事件
short revents;//实际注册的事件,由内核填充
}
第二个参数fdns是被监听的文件描述符的总数.
第三个参数就是timeout,就是poll的超时值。如果timeout为-1,poll就会永远阻塞,直到有某个事件发生。timeout为0时,poll调用就会立刻返回。
二.poll支持的事件类型
三.代码实现
客户端:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<assert.h>
#include<string.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
int main()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
assert(sockfd != -1);
struct sockaddr_in saddr;
memset(&saddr,0,sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(6666);
saddr.sin_addr.s_addr = inet_addr("127.0.0.1");
int res = connect(sockfd,(struct sockaddr*)&saddr,sizeof(saddr));
assert(res != -1);
while(1)
{
char buff[128] = {0};
printf("input:\n");
fgets(buff,128,stdin);
if(strncmp(buff,"end",3) == 0)
{
break;
}
send(sockfd,buff,strlen(buff),0);
memset(buff,0,128);
recv(sockfd,buff,127,0);
printf("buff = %s\n",buff);
}
close(sockfd);
}
服务器端:
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<poll.h>
#include<string.h>
#include<unistd.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#define MAXFD 10
void fds_init(struct pollfd fds[]);
void fds_add(struct pollfd fds[],int fd);
void fds_del(struct pollfd fds[],int fd);
int create_socket();
int main()
{
int sockfd = create_socket();
assert(sockfd != -1);
struct pollfd fds[MAXFD];
fds_init(fds);//初始化fd = -1
fds_add(fds,sockfd);
while(1)
{
int n = poll(fds,MAXFD,5000);
if(n == -1)
{
perror("poll error\n");
continue;
}
else if(n == 0)
{
printf("time out\n");
continue;
}
else
{
int i = 0;
for(;i<MAXFD;i++)
{
int fd = fds[i].fd;
if(fd == -1)
{
continue;
}
if(fds[i].revents & POLLIN)
{
if(fd == sockfd)//监听套接字
{
struct sockaddr_in caddr;
int len = sizeof(caddr);
int c = accept(sockfd,(struct sockaddr*) &caddr,&len);
if(c < 0)
{
continue;
}
printf("accept :%d\n",c);
fds_add(fds,c);
}
else//普通套接字
{
char buffer[128] = {0};
if((recv(fd,buffer,127,0))<= 0)
{
close(fd);
fds_del(fds,fd);
printf("one client over\n");
continue;
}
printf("buffer %d = %s\n",fd,buffer);
send(fd,"ok",2,0);
}
}
// if(fds[i].revents & POLLOUT)
}
}
}
}
void fds_init(struct pollfd fds[])
{
int i=0;
for(;i<MAXFD;i++)
{
fds[i].fd = -1;
fds[i].events = 0;
fds[i].revents = 0;
}
}
void fds_add(struct pollfd fds[],int fd)
{
int i = 0;
for(;i<MAXFD;i++)
{
if(fds[i].fd == -1)
{
fds[i].fd = fd;
fds[i].events = POLLIN;
break;
}
}
}
void fds_del(struct pollfd fds[],int fd)
{
int i = 0;
for(;i<MAXFD;i++)
{
if(fds[i].fd == fd)
{
fds[i].fd = -1;
fds[i].events = 0;
break;
}
}
}
int create_socket()
{
int sockfd = socket(AF_INET,SOCK_STREAM,0);
if(sockfd == -1)
{
return -1;
}
struct sockaddr_in saddr;//专用的地址结构
memset(&saddr,0,sizeof(saddr));//将saddr置为0
saddr.sin_family=AF_INET;
saddr.sin_port=htons(6666);
saddr.sin_addr.s_addr=inet_addr("127.0.0.1");
int res = bind(sockfd,(struct sockaddr *)&saddr,sizeof(saddr));//命名函数,将saddr 的地址分配给未命名的sockfd文件描述符
if(res == -1)
{
return -1;
}
listen(sockfd,5);
return sockfd;
}
四.结果展示