朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll模型

        在《朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型》中,我们分析了它只能支持1024个连接同时处理的原因。但是在有些需要同时处理更多连接的情况下,1024个连接往往是不够的,也就是不能够高并发。那么这个时候我们就可以采用本文介绍的Poll模型。(转载请指明出于breaksoftware的csdn博客)

        在使用Poll模型之前,我们需要定义一个保存连接信息的数组

	struct pollfd fds[FDS_COUNT];

        之后创建异步监听socket、绑定端口和监听端口等行为和《朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型》一文中一模一样,本文就不列出代码了。我们把创建的socket信息赋值给fds数组的第一个元素,并且设定我们需要关注的事件POLLIN——可读。

	int timeout;
	int cur_fds_count;
	int rc;
	int index;
	int expect_events;
	int error_events;
……// 创建socket
	memset(fds, 0, sizeof(fds));

	error_events = POLLERR | POLLNVAL;
	expect_events = POLLIN;
	
	fds[0].fd = listen_sock;
	fds[0].events = expect_events;
	cur_fds_count = 1;

        cur_fds_count用于记录当前fds数组中有多少个被关心的文件描述符。因为一开始我们只关心监听socket,所以它的初始值是1。

        然后我们就要在一个死循环中,不停的调用poll函数,监控我们关心的文件描述符是否发生了状态改变

	timeout = (500);	

	while (1) {
		rc = poll(fds, cur_fds_count, timeout);
		if (rc < 0) {
			perror("poll error\n");
			exit(EXIT_FAILURE);
		};
		if (rc == 0) {
			//perror("poll timeout\n");
		};

        poll函数的返回值和select函数类似。如果返回小于0,则说明发生了错误,我们让程序退出。如果返回了0,则说明poll函数超时。如果大于0,则说明被关心的文件描述符状态发生了改变。但是此时,我们仍然不知道是哪个文件描述符发生了改变,所以我们要遍历fds数组。

		int cur_fds_count_temp = cur_fds_count;
		for (index = 0; index < cur_fds_count_temp; ++index) {

        这个时候我们有必要说明下pollfd结构体的定义

struct pollfd {
	int fd;
	short events;
	short revents;
};

        pollfd中的fd是用于记录我们关心的文件描述符;events表示我们关心的事件,如POLLIN、POLLOUT等。revents是实际发生的事件。于是我们一开始要判断改pollfd是否发生了事件改变

			if (fds[index].revents == 0) {
				continue;
			}

        接着我们判断下发生的事件是否是我们定义的出错事件。

			if (fds[index].revents & error_events) {
				perror("revents error");

        如果出错的是监听socket,则我们退出程序。

				if (fds[index].fd == listen_sock) {
					perror("listen sock error");
					exit(EXIT_FAILURE);
				}

        如果出错的不是监听socket,则它就是客户端接入的socket,我们将它关闭,并且将fds数组的最后一个元素覆盖当前位置,让数组长度减一。这个过程是最精简的数组缩小方式,如果使用新数组去记录,将导致效率降低。

				else {
					close(fds[index].fd);
					cur_fds_count--;
					if (index < cur_fds_count) {
						memcpy(&fds[index], &fds[cur_fds_count], sizeof(fds[cur_fds_count]));
						memset(&fds[cur_fds_count], 0, sizeof(fds[cur_fds_count]));
						index--;
					}
					cur_fds_count_temp--;
					continue;
				}	
			}

        我们再看发生的事件是否是我们关心的事件,如果不是,则continue掉,继续处理下一个pollfd。

			if (!(fds[index].revents & fds[index].events)) {
				continue;
			}

        经过上述筛选,剩下的就是我们要正常处理的pollfd了。和Select方式一样,我们先看看其是否是监听socket。如果是,则获取客户端接入的socket值,并记录到数组中。

			if (fds[index].fd == listen_sock) {
				int new_sock;
				new_sock = accept(listen_sock, NULL, NULL);
				if (new_sock < 0) {
					//perror("accept error");
					if (errno != EWOULDBLOCK) {
						continue;
					}
					exit(EXIT_FAILURE);
				}
				else {
					request_add(1);
					//set_block_filedes_timeout(new_sock);
					if (cur_fds_count + 1 < sizeof(fds)) {
						fds[cur_fds_count].fd = new_sock;
						fds[cur_fds_count].events = expect_events;
						cur_fds_count++;
					}
				}
			}

        如果不是监听socket,则是客户端接入的socket。我们就读取这个socket中的内容,并写入我们的回包。

			else {
				if (0 == server_read(fds[index].fd)) {
					server_write(fds[index].fd);
				}
				close(fds[index].fd);
				cur_fds_count--;
				if (index < cur_fds_count) {
					memcpy(&fds[index], &fds[cur_fds_count], sizeof(fds[cur_fds_count]));
					memset(&fds[cur_fds_count], 0, sizeof(fds[cur_fds_count]));
					index--;
				}
				cur_fds_count_temp--;
			}
		}
	}
	return 0;
}

        如此,我们便将简单的poll服务器给写完了。我们看下poll模型的处理能力。采用和《朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型》一文中相同的环境和压力,我们看下服务器的数据输出

        再看下客户端的输出

       可见当前环境下poll模型的处理能力大概是每秒7500次请求。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

breaksoftware

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值