linux编程:多路转接IO之poll模型
poll模型
针对每个描述符构建一个事件结构,描述要监控的是哪个描述符,监控的是什么IO状态,然后在内核中进行遍历判断
接口和参数
- int poll(struct pollfd *fds, nfds_t nfds, int timeout);
- fds:描述符的事件结构数组首地址,struct pollfd( int fd–监控的描述符;short events–要监控的事件;short revents–实际就绪事件;}
- nfds: fds参数中监控的有效事件结构的个数
- timeout:超时时间-单位:毫秒
- 返回值:返回值小于0表示监控出错;返回值等于0表示监控超时;返回值大于0表示就绪的事件个数
操作流程
- 程序员定义事件结构体数组,将需要监控的描述符以及对应事件填充到数组中
struct pollfd {int fd-描述符;short events-要监控的事件POLLIN/POLLOUT; short revents-实际就绪的事件;}- 调用poll开始监控,将结构体数组拷贝到内核中,进行轮训遍历判断,等到有描述符就绪或者超时则调用返回,返回前记录描述符实际就绪事件到节点中
int poll(struct pollfd *fds,int nfds,int timeout)- 调用返回后程序遍历结构体数组,根据每个节点中的实际就绪事件判断当前描述符是否就绪的指定的事件,进而进行操作
for(int i = 0; i <= nfds; i++) if (fds[i].revents & POLLIN){ fds[i].fd就绪了可读事件}
优缺点分析
- 优点:采用事件结构进行监控,简化了多个描述符集合的操作流程;所能监控的描述符数量上限不做限制;不用每次重新组织事件结构
- 缺点:跨平台移植性较差,监控原理是轮询遍历性能会随着描述符的增多而下降
poll编程
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<netinet/in.h>//地址结构
#include<arpa/inet.h>//字节序转换接口
#include<sys/socket.h>//套接字接口
#include<poll.h>
#define MAX_LISTEN_NUM 5
int main()
{
//1.创建套接字
int sockfd=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
if(sockfd<0){
perror("socket error");
return -1;
}
//2.绑定地址信息
struct sockaddr_in addr;
addr.sin_family=AF_INET;
addr.sin_port=htons(9000);
addr.sin_addr.s_addr=inet_addr("0.0.0.0");
socklen_t len=sizeof(addr);
int ret = bind(sockfd,(struct sockaddr*)&addr,len);
if(ret<0){
perror("bind error");
return -1;
}
//3.开始监听
ret =listen(sockfd,MAX_LISTEN_NUM);
if(ret<0){
perror("listen error");
return -1;
}
struct pollfd fds[10];
for(int i=0;i<10;i++)
fds[i].fd=-1;
fds[0].fd=sockfd;
fds[0].events=POLLIN;//监控sockfd可读事件
//4.获取新连接
while(1){
struct pollfd tmpfds[10];
int fd_num=0;
for(int i=0;i<10;i++){
if(fds[i].fd!=-1){
memcpy(&tmpfds[fd_num],&fds[i],sizeof(struct pollfd));
fd_num++;
}
}
int ret=poll(tmpfds,fd_num,3000);
if(ret<0){
perror("poll error");
continue;
}else if(ret==0){
printf("No descriptor ready,wait timeout\n");
continue;
}
for(int i=0;i<fd_num;i++){
if(tmpfds[i].revents&POLLIN){//可读事件
if(tmpfds[i].fd==sockfd){//监听描述符有新连接
struct sockaddr_in cliaddr;
int newfd=accept(sockfd,(struct sockaddr*)&cliaddr,&len);
if(newfd<0){
perror("accept error");
continue;
}
for(int i=0;i<10;i++){
if(fds[i].fd==-1){
fds[fd_num].fd=newfd;
fds[fd_num].events=POLLIN;
}
}
}else{
//5.使用新连接与客户端通信(收发数据)
char buf[1024]={0};
//接收数据
ret=recv(fds[i].fd,buf,1023,0);
if(ret<=0){
perror("recv error");
close(tmpfds[i].fd);
fds[i].fd=-1;
continue;
}
printf(" client say:%s\n",buf);
//发送数据
ret=send(fds[i].fd,buf,strlen(buf),0);
if(ret<0){
perror("send error");
close(fds[i].fd);
continue;
}
}
}
}
}
//6.关闭套接字
close(sockfd);
return 0;
}
通信演示
poll_server
client客户端