poll
poll 系统调用和 select 类似,也是在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者。poll 的原型如下:
#include <poll.h>
int poll (struct pollfd* fds, int nfds, int timeout);
fds:是一个 pollfd 结构类型的数组,它指定所有我们感兴趣的文件描述符上发生的可读、可写和异常等事件。传递的是数组首地址,数组由用户定义。
nfds:数组的长度,元素的个数,用户关注的文件描述符的个数
timeout:超时时间 -1 代表永久阻塞直到有就绪事件发生
返回值
-1 出错
=0 超时
>0 就绪文件描述符的个数
pollfd 结构体的定义如下:
struct pollfd
{
int fd; 用户关注的文件描述符
short events; 注册的文件描述符上的那种事件
short revents; 由内核修改,返回此文件描述符上发生的事件类型(必须是events指定的关注的事件)
};
poll 和 select 的不同点
1、用户关注的事件类型更多
2、将用户关注的事件与内核修改的就绪事件分隔开,每次调用 poll 不需要重新设置
3、文件描述符不再是按位表示,直接用 int 类型,文件描述符的取值范围扩大到系统最大限制 655353。
3.1 用户关注的文件描述符可以更大
3.2 用户关注的文件描述符个数由用户自己制定,能扩大到系统最大限制
1、poll 返回时,也是将用户关注的所有文件描述符返回。poll 探测就绪文件描述符的时间复杂度是 O(n),poll 返回后依旧需要循环检测哪些文件描述符
2、内核依旧是采用轮询方式处理
Sever
#define _GNU_SOURCE
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <poll.h>
#define FDMAXNUM 100
void InitFds(struct pollfd *fds)
{
int i=0;
for(;i<FDMAXNUM;i++)
{
fds[i].fd=-1;
}
}
void DeleteFd(int fd, struct pollfd *fds)
{
int i=0;
for(;i<FDMAXNUM;i++)
{
if(fds[i].fd==fd)
{
fds[i].fd=-1;
break;
}
}
}
void AddFd(int fd, struct pollfd *fds)
{
int i=0;
for(;i<FDMAXNUM;i++)
{
if(fds[i].fd==-1)
{
fds[i].fd=fd;
fds[i].events=POLLIN|POLLRDHUP;
break;
}
}
}
void GetClientLink(int fd, struct pollfd *fds)
{
struct sockaddr_in cli;
int len=sizeof(cli);
int c=accept(fd,(struct sockaddr*)&cli,&len);
if(c==-1)
{
return;
}
printf("accept(%d)\n",c);
AddFd(c,fds);
}
void DealClientData(int fd, struct pollfd *fds, int rdhup)
{
if(rdhup)
{
close(fd);
printf("one client over\n");
DeleteFd(fd,fds);
return;
}
char buff[128]={0};
int n=recv(fd,buff,127,0);
if(n<=0)
{
DeleteFd(fd,fds);
return ;
}
printf("buff=%s",buff);
send(fd,"OK",2,0);
}
int main()
{
int listenfd = socket(AF_INET, SOCK_STREAM, 0);
assert(listenfd != -1);
struct sockaddr_in ser;
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(listenfd, (struct sockaddr*)&ser, sizeof(ser));
assert(res != -1);
listen(listenfd, 5);
struct pollfd fds[FDMAXNUM];
InitFds(fds);
fds[0].fd=listenfd;
fds[0].events=POLLIN;
while(1)
{
int a=poll(fds,FDMAXNUM,-1);
if(a<=0)
{
perror("poll error\n");
exit(0);
}
if(a>0)
{
int i=0;
for(;i<FDMAXNUM;i++)
{
if(fds[i].fd==-1)
{
continue;
}
if(fds[i].fd==listenfd && fds[i].revents & POLLIN)
{
GetClientLink(fds[i].fd, fds);
}
else if(fds[i].revents & POLLIN)
{
if(fds[i].revents & POLLRDHUP)
DealClientData(fds[i].fd,fds,1);
else
DealClientData(fds[i].fd,fds,0);
}
}
}
}
}
Cli
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <unistd.h>
#include <string.h>
#include <netinet/in.h>//字节序列转换函数所用
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>//地址转换函数所用
int main()
{
int sockfd=socket(AF_INET,SOCK_STREAM,0);
assert(-1!=sockfd);
struct sockaddr_in ser;
memset(&ser,0,sizeof(ser));
ser.sin_family=AF_INET;
ser.sin_port=htons(6000);
inet_aton("127.0.0.1",(struct in_addr*)&ser.sin_addr);
int res=connect(sockfd,(struct sockaddr*)&ser,sizeof(ser));
assert(-1!=res);
while(1)
{
printf("please input: ");
char data[128]={0};
fgets(data,128,stdin);
if(strncmp(data,"bye",3)==0)
{
close(sockfd);
break;
}
send(sockfd,data,strlen(data)+1,0);
char buff[128]={0};
int n=recv(sockfd,buff,127,0);
if(n<=0)
{
printf("error\n");
close(sockfd);
break;
}
printf("n==%d: %s\n",n,buff);
}
}