int poll(struct pollfd *fds, nfds_t nfds, int timeout);
函数说明:与select类似,委托内核监控可读,可写,异常事件。
函数说明:
fds:一个struct pollfd结构体数组的首地址
struct pollfd {
int fd; 要监控的文件描述符
short events; 输入参数,告诉内核要监控的可读,可写,异常事件
short revents; 输出参数,内核告诉应用程序改变的文件描述符
};events/revents:
POLLIN :可读事件POLLOUT:可写事件
POLLERR:异常事件
nfds:要监控的文件描述符的数量,最大数组下标+1
timeout:
=0:不阻塞,立刻返回
-1:表示一直阻塞,直到事件发生
>0:表示阻塞时长,在时长范围内若有事件发生就会立刻返回
时间到也会返回。
开发流程:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<arpa/inet.h>
#include <sys/types.h>
#include <netinet/in.h>
#include<ctype.h>
#include <poll.h>
#include <signal.h>
#include<errno.h>
int main()
{
//int socket(int domain, int type, int protocol);
int lfd=socket(AF_INET,SOCK_STREAM ,0);
if(lfd<0)
{
perror("socket error");
return -1;
}
// int setsockopt(int sockfd, int level, int optname,const void *optval, socklen_t optlen);
int opt=1;
setsockopt(lfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(int));
//int bind(int sockfd, const struct sockaddr *addr,socklen_t addrlen);
struct sockaddr_in sev;
sev. sin_family=AF_INET;
sev. sin_port=htons(8888);
inet_pton(AF_INET,"192.168.230.130",&sev.sin_addr.s_addr);
int ret=bind(lfd,(struct sockaddr*)&sev,sizeof(sev));
if(ret<0)
{
perror("bind error");
return -1;
}
ret=listen(lfd,128);
if(ret<0)
{
perror("listen error");
return -1;
}
struct pollfd fds[1024];//定义数组
int i;
int cfd;
int nready;
int maxi = 0;一开始的最大下标
int x;
int j;
int sockfd;
char buf[64];
int n;
for(i=0;i<1024;i++)
{
fds[i].fd=-1;//把数组的fd全部初始化为-1,表示该位置空闲
}
fds[0].fd=lfd;//在位置零处存放lfd监听文件描述符,让内核监控lfd
fds[0].events=POLLIN;//监听可读事件
struct sockaddr_in client;//客户端地址
socklen_t len;
len=sizeof(client);
char sIP[16];
while(1)
{
nready=poll(fds,maxi+1,-1); //maxi表示监控文件描述符的数量
if(nready < 0)
{
if(errno==EINTR)//被信号打断
{
continue;
}
break;
}
if(fds[0].revents==POLLIN)//lfd发生变化,有客户端请求连接
{
cfd=accept(lfd,(struct sockaddr*)&client,&len);
for(i=1;i<1024;i++)//遍历数组元素
{
if(fds[i].fd==-1)//值为-1说明位置空闲
{
memset(sIP,0x00,sizeof(sIP));
printf("client:ip==[%s],port==[%d] is connect\n",inet_ntop(AF_INET,&client.sin_addr.s_addr,sIP,sizeof(sIP)),ntohs(client.sin_port));
fds[i].fd=cfd;//加入数组,让内核监控此cfd
fds[i].events=POLLIN;//监控可读事件
break;//退出for循环
}
}
if(i==1024)
{
printf("there is no space\n");
close(cfd);
continue;
}
if(maxi < i)
{
maxi = i;//更新最大数组下标
}
if(--nready==0)
{
continue;//可读事件为0,继续while循环,不用进行下面的操作
}
}
for(x=1;x<=maxi;x++)//从数组开始循环
{
sockfd=fds[x].fd;
if(sockfd==-1)
{
continue;//无效,继续循环
}
if(fds[x].revents==POLLIN)
{
memset(buf,0x00,sizeof(buf));
n=read(sockfd,buf,sizeof(buf));
if(n<=0)
{
printf("read error or client close, n==[%d]\n",n);
close(sockfd);//关闭文件描述符
fds[x].fd=-1;//移除,让内核不监控此文件描述符
}
else //n>0的情况
{
printf("[%d]n==[%d],buf==[%s]\n",x,n,buf);
for(j=0;j<n;j++)
{
buf[j]=toupper(buf[j]);
}
write(sockfd,buf,n);
}
if(--nready==0)
{
break;
}
}
}
}
close(lfd);
return 0;
}
结果:
由结果可见 删除1位置时,重新连接的客户端是从位置1连接的,说明数组位置充分使用
注意:
1 当poll函数返回时,结构体当中的fd和events没有发生变化,究竟有没有事件发生由revents来判断,所以poll是请求和返回分离。
2 struct pollfd结构体中fd成员赋值为-1,则内核不会对这个文件描述符进行监控