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监听文件描述符,让内核监控lfdfds[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;//加入数组,让内核监控此cfdfds[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,则内核不会对这个文件描述符进行监控