linux下的I/O复用模型之poll详解

  1. poll函数
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
参数:
(1)结构体数组
(2)监听的最大个数
(3)时间,当值为负数时表示阻塞
结构体:
struct pollfd {
	int   fd;         文件描述符
	short events;     传入参数:监听的事件
	short revents;    传出参数:监听事件中满足条件返回的事件
};
传入参数的设置常用的有
POLLIN:监听读事件
POLLOUT:监听写事件

  1. poll_server代码:
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<sys/socket.h>
#include<sys/types.h>
#include<sys/select.h>
#include<arpa/inet.h>
#include<poll.h>

int main()
{
	//网络初始化
	char recvbuf[1024];
	char ip[16];
	int  ret;
	struct pollfd clientfdArr[1024];

	struct sockaddr_in addrserver,addrclient;
	bzero(&addrserver,sizeof(addrserver));
	bzero(&recvbuf,sizeof(recvbuf));
	bzero(&ip,sizeof(ip));

	addrserver.sin_family = AF_INET;
	addrserver.sin_port = htons(27015);
	addrserver.sin_addr.s_addr = htonl(INADDR_ANY);

	//创建socket
	int serverfd = socket(AF_INET,SOCK_STREAM,0);
	if(serverfd == -1){
		printf("create socket error\n");
		return 1;
	}
	//绑定
	ret = bind(serverfd,(struct sockaddr*)&addrserver,sizeof(addrserver));
	if(ret == -1)
	{
		printf("bind error\n");
		close(serverfd);
		return 1;
	}
	//监听
	ret = listen(serverfd,128);
	if(ret == -1)
	{
		printf("listen error\n");
		close(serverfd);
		return 1;
	}
	printf("Poll Server Runing.....\n");
	

	//初始化socket描述符数组
	for(int i = 0; i < 1024; ++i)
		clientfdArr[i].fd = -1;
	clientfdArr[0].fd = serverfd;
	clientfdArr[0].events = POLLIN;
	
	while(1)
	{
				
		ret = poll(clientfdArr,1024,-1);
		if(ret > 0)
		{
			printf("poll successful...\n");
			printf("就绪的数量为:%d\t事件:某客户端请求连接.....\n",ret);
			
			//serverfd就绪
			if(clientfdArr[0].revents == POLLIN)
			{
				socklen_t size = sizeof(addrclient);
				int clientfd = accept(serverfd,(struct sockaddr*)&addrclient,&size);
				if(clientfd == -1){
					printf("accept error\n");
					continue;
				}
				//将新的clientfd加入监听集合
				for(int i = 0; i < 1024; ++i)
					if(clientfdArr[i].fd == -1){
						clientfdArr[i].fd = clientfd;
						clientfdArr[i].events = POLLIN;
						break;
					}
			}

			//clientfd就绪,数据传输
			else
			{
				for(int i = 0; i < 1024; ++i)
				{
					if(clientfdArr[i].fd != -1)
					{
						if(clientfdArr[i].revents == POLLIN)
						{
							ret = recv(clientfdArr[i].fd,recvbuf,sizeof(recvbuf),0);
							if(ret > 0)
							{
								printf("recv data is:%s\n",recvbuf);
								continue;
							}
							else if(ret == 0)
							{
								printf("client normal closed...\n");
								close(clientfdArr[i].fd);
								clientfdArr[i].fd = -1;
								break;
							}
							else{
								printf("recv error\n");
								close(clientfdArr[i].fd); 
								clientfdArr[i].fd = -1;
								break;
							}
						}
					}
				}
			}
		}
		else
		{
			printf("select error\n");
			break;
		}

	}

	close(serverfd);
	return 0;
}


  1. 测试结果:

在这里插入图片描述

  1. 模型评价:
    poll本质上和select没有区别,只是取消了最大监控文件描述符数限制和将传入传出分离,并没有从根本上解决select存在的问题.它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设d备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历.

    它没有最大连接数的限制,原因是它是基于链表来存储的,但是同样有一个缺点:
    (1)大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
    (2)poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
    注意:select 和 poll 都需要在返回后,通过遍历文件描述符来获取已经就绪的socket。事实上,同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长,其效率也会线性下降。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值