【Linux】高性能服务器编程(三)——I/O复用之poll

前篇介绍的是关于I/O复用的方式之一select。见select

本文将及诶少第二种方式poll;

一、poll

1、函数原型

Int poll(struct pollfd *fds, int nfds, int timeout);

Fds:指定被监听的事件的集合;传的是数组首地址,由用户定义;大小是由nfds来确定;

nfds:指定被监听事件集合fds的大小,

Timeout:超时时间;

                   >0 就绪文件描述符的个数

                   -1 出错

                    0 超时

2、Poll和select的区别:

         (1)用户关注的事件类型更多:select中只能利用readfds、writefd和excefds来记录这对应的三种事件,而poll可以利用pollfd中的events来设置事件;

         (2)内核修改的和用户关注的分开表示,每次调用不需要重新设置;

         (3)poll中文件描述符不再是按位设置,而是直接用int来设置;

                            用户关注的文件描述符的值可以更大;

                           用户关注的文件描述符的个数由用户数组决定,所以个数会更多,65535;

         (4)Poll将用户关注的事件和内核修改的事件分开,每次调用都不需要重置;

         (5)Poll和select返回的都是就绪和未就绪的,遍历就绪的文件描述符时间复杂度为O(n)。用户程序依旧需要循环检测哪些文件描述符就绪。

3、poll中用户关注的事件类型

红色部分是我们常用的几种事件。

4、poll的优缺点:

优点:

         将用户关注的文件描述符的事件单独表示,可关注更多的事件类型;

         将用户传递的和内核修改的分开,每次调用poll之前,不需要重新设置;

         Poll函数没有最大文件描述符的限制;

缺点:

         每次调用都需要将用户空间数组拷贝到内核中;

         每次返回都需要将所有的文件描述符拷贝到用户空间数组中,无论是否就绪;

         返回的是所有的文件描述符,搜索就绪文件描述符的时间复杂度为O(n);

二、poll服务器端的简单实现

1、代码

#define _GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>

#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<poll.h>
#define SIZE 100
void Init_fds(struct pollfd *fds)//初始化fds结构
{
	int i = 0;
	for(;i < SIZE;++i)
	{
		fds[i].fd = -1;
		fds[i].events = 0;
		fds[i].revents = 0;
	}
}
void Insert_fd(struct pollfd*fds,int fd,short event)//添加文件描述符以及对应的事件
{
	int i = 0;
	for(;i < SIZE;++i)
	{
		if(fds[i].fd == -1)
		{
			fds[i].fd = fd;
			fds[i].events = event;
			break;
		}
	}
}
void Delete_fd(struct pollfd *fds,int fd)//删除文件描述符以及对应的事件
{
	int i = 0;
	for(;i < SIZE;++i)
	{
		if(fds[i].fd == fd)
		{
			fds[i].fd = -1;
			fds[i].events = 0;
			break;
		}
	}
}
int main()
{
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd != -1);

	struct sockaddr_in ser,cli;
	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(sockfd,(struct sockaddr*)&ser,sizeof(ser));
	listen(sockfd,5);

	struct pollfd fds[SIZE];
	Init_fds(fds);
	Insert_fd(fds,sockfd,POLLIN);

	while(1)
	{

		int n = poll(fds,SIZE,-1);
		if(n <= 0)
		{
			printf("poll error\n");
			continue;
		}

		//有文件描述符就绪
		int i = 0;
		for(;i < SIZE;++i)//遍历数组
		{
			if(fds[i].fd != -1) //判断就绪
			{
				int fd = fds[i].fd;
				if(fds[i].revents & POLLRDHUP)
				{
					printf("%d will close\n",fd);
					close(fd);
					Delete_fd(fds,fd);
				}
				else if(fds[i].revents & POLLIN)
				{
					if(fd == sockfd)//链接事件
					{
						int len = sizeof(cli);
						int c = accept(fd,(struct sockaddr*)&cli,&len);
						if(c < 0)
						{
							continue;
						}
						Insert_fd(fds,c,POLLIN | POLLRDHUP);
					}
					else//接收数据
					{
						char buff[128] = {0};
						recv(fd,buff,127,0);
						printf("%d,%s\n",fd,buff);
						send(fd,"OK",2,0);
					}
				}
			}
		}
	}
	return 0;
}

2、结果展示:

小结poll:

poll相比select它能够处理的事件类型更多,范围更大,并且poll是将用户关注的事件和内核修改的事件分开。每次调用都不需要重置。

当有事件来临时,poll在用户态将事件的fd以及用户关注的事件插入到fds结构体数组中的某一个元素下。然后将数组fds(其中包括就绪的和未就绪的事件)拷贝到内核态中,内核识别具体所作的事件并且进行处理,处理完成后内核对对应fd中的revent进行修改,然后将修改过的所有的fds(包括就绪的和未就绪的事件)拷贝到用户态中。用户程序检测就绪文件描述符的时间复杂度为O(N)。这一次事件的执行就到此结束。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
linux C语言 网络编程教程及源码 一、网络应用层编程 1、Linux网络编程01——网络协议入门 2、Linux网络编程02——无连接和面向连接的区别 3、Linux网络编程03——字节序和地址转换 4、Linux网络编程04——套接字 5、Linux网络编程05——C/S与B/S架构的区别 6、Linux网络编程06——UDP协议编程 7、Linux网络编程07——广播 8、Linux网络编程08——多播 9、Linux网络编程09——TCP编程之客户端 10、Linux网络编程10——TCP编程服务器 11、Linux网络编程11——tcp、udp迭代服务器 12、Linux网络编程12——tcp次握手、四次挥手 13、Linux网络编程13——connect()、listen()和accept()者之间的关系 14、Linux网络编程14——I/O复用之select详解 15、Linux网络编程15——I/O复用poll详解 16、Linux网络编程16——I/O复用之epoll详解 17、Linux网络编程17——tcp并发服务器(多进程) 18、Linux网络编程18——tcp并发服务器(多线程) 19、Linux网络编程——tcp高效并发服务器(select实现) 20、Linux网络编程——tcp高效并发服务器poll实现) 21、Linux网络编程——tcp高效并发服务器(epoll实现) 二、网络底层编程(黑客模式) 1、Linux网络编程1——啥叫原始套接字 2、Linux网络编程2——原始套接字编程 3、Linux网络编程3——原始套接字实例:MAC头分析 4、Linux网络编程4——原始套接字实例:MAC地址扫描器 5、Linux网络编程5——IP数据报格式详解 6、Linux网络编程6——TCP、UDP数据包格式详解 7、Linux网络编程7——原始套接字实例:发送UDP数据包 8、Linux网络编程8——libpcap详解 9、Linux网络编程9——libnet详解

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值