当前位置: 首页 > 编程日记 > 正文

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

在《朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型》中我们分析了朴素模型的一个缺陷——一次只能处理一个连接。本文介绍的Select模型则可以解决这个问题。(转载请指明出于breaksoftware的csdn博客)

和朴素模型一样,我们首先要创建一个监听socket,然后调用listen去监听服务器端口。不同的是,我们要对make_socket方法传递1,因为我们要创建一个异步的socket。

	listen_sock = make_socket(1);if (listen(listen_sock, SOMAXCONN) < 0) {perror("listen error");exit(EXIT_FAILURE);}

下一步我们需要清空Select模型使用的fd_set结构体对象,并把监听socket设置进去

	FD_ZERO(&active_fd_set);FD_SET(listen_sock, &active_fd_set);

active_fd_set用于记录活动的socket信息。之后我们使用到的read_fd_set则是其一个拷贝,因为我们只关心读行为

	fd_set active_fd_set, read_fd_set;

和朴素模型类似,我们也需要使用一个死循环让服务器不要停止

	/* Initialize the set of active sockets. */while (1) {timeout.tv_sec = 0;timeout.tv_usec = 500;/* Service all the sockets with input pending. */read_fd_set = active_fd_set;switch(select(FD_SETSIZE, &read_fd_set, NULL, NULL, &timeout)) {case -1 : {perror("select error\n");exit(EXIT_FAILURE);}break;case 0 : {//perror("select timeout\n");}break;default: {

select函数第一个参数我们传递了FD_SETSIZE,它在我的系统上是1024,它代表需要关注的socket的最大个数。第二参数是用于记录需要关注的发生读事件的fd_set对象。我们让select函数按异步方式执行,故最后一个参数设置为500微秒的超时时间。整个select函数意思是我们需要等待socket发生可读事件,如果等待时间超过超时设置,则函数返回0,如果出错则返回-1,如果等待到事件则返回其他值。

			default: {for (index = 0; index < FD_SETSIZE; ++index) {if (FD_ISSET(index, &read_fd_set)) {if (listen_sock == index) {/* Connection request on original socket. */int new_sock;new_sock = accept(listen_sock, NULL, NULL);if (new_sock < 0) {perror("accept error");exit(EXIT_FAILURE);}request_add(1);//set_block_filedes_timeout(new_sock);FD_SET(new_sock, &active_fd_set);} else {if (0 == server_read(index)) {server_write(index);}close(index);FD_CLR(index, &active_fd_set);}}}}}}return 0;
}

default中才是我们程序的重点。

我们使用一个for循环遍历每个socket。如果该socket通过FD_ISSET宏判断不处于我们关注的可读事件fd_set中,则忽略它。

如果处在可读fd_set中,则看看其是否是监听socket。
        如果是监听socket,则使用accpet方法获取接入的socket。并使用request_add让请求数量加一。还要使用FD_SET宏将该socket加入到活动状态的fd_set中。之后该活动状态的fd_set将被赋值给需要关注可读事件的fd_set中。

如果不是监听socket,则是接入的socket。于是我们调用《朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型》一文中介绍的server_read和server_write方法读取内容并回包。最后我们还要关闭socket,并使用FD_CLR宏将该socket从活动状态的fd_set中去掉。之后的select函数将不会在关注该socket了。

整个过程非常简单。但是这其中却包含了很多值得思考的问题。

首先我抛出一个问题,我在default中使用了一个从0到FD_SETSIZE的遍历行为。并且将遍历的游标——index作为socket去操作——使用server_read和server_write去读取。于是问题就来了,使用make_socket创建的socket值和使用accept接收到的socket的值怎么和游标产生关联?代码中似乎没有任何让它们产生关联的逻辑,而且它们的关系是严格的“相等”的关系!那么只有一个假设,就是make_socket和accept返回的socket值在FD_SETSIZE和0之间。但是目前我没有找到文档对这个问题进行说明,而我也没深入研究这两个函数考证到其值就是在这个范围之内,那么为什么我还要这么去用呢?

我们先记下这个问题,深入到linux的源码中取解释这个使用的正确性。

我们先看下fd_set的定义

/* The fd_set member is required to be an array of longs.  */
typedef long int __fd_mask;/* Some versions of <linux/posix_types.h> define this macros.  */
#undef	__NFDBITS
/* It's easier to assume 8-bit bytes than to get CHAR_BIT.  */
#define __NFDBITS	(8 * (int) sizeof (__fd_mask))
#define	__FD_ELT(d)	((d) / __NFDBITS)
#define	__FD_MASK(d)	((__fd_mask) 1 << ((d) % __NFDBITS))/* fd_set for select and pselect.  */
typedef struct{/* XPG4.2 requires this member name.  Otherwise avoid the namefrom the global namespace.  */
#ifdef __USE_XOPEN__fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
#else__fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
#endif} fd_set;/* Maximum number of file descriptors in `fd_set'.  */
#define	FD_SETSIZE		__FD_SETSIZE

以上我是在我ubuntu系统的/usr/include/x86_64-linux-gnu/sys/select.h文件中找到的定义。我们看到fd_set的主体就是一个long int型数组__fds_bits。该数组的个数是两个数的商。被除数__FD_SETSIZE就是我们程序中使用的FD_SETSIZE,也就是1024。除数__NFDBITS是64。于是fd_set中数组元素的个数是1024/64=16。注意一下这个值是16,而我们程序中关注的socket的最大个数是FD_SETSIZE——1024,这是为什么?其实这就是该结构设计的一个精妙之处。fd_set的__fds_bits是一个16个元素的long int型数组,其总长度就是16*64=1024位。于是可以使用每一位表示一个socket。

我们到/usr/include/x86_64-linux-gnu/bits/select.h 文件中看看linux是如何让socket和这个空间中每一位进行对应的。我们查看FD_SET宏

#define __FD_SET(d, set) \((void) (__FDS_BITS (set)[__FD_ELT (d)] |= __FD_MASK (d)))

__FDS_BITS宏定义在fd_set定义中。它就是返回fd_set的__fds_bits数组首地址。数组的游标是通过__FD_ELT对socket进行处理的结果。__FD_ELT在上面我们已经见过,它是对socket值除以__NFBITS——64的值。通过游标我们取到数组元素值之后,我们再用其与__FD_MASK对socket进行操作的值进行或操作。__FD_MASK的定义也在上面给出,它是将socket的值与__NFBITS——64相除,取得余数,然后让1左移该余数次。这样我们就将该socket映射到fd_set内存的一位中。我们知道,只要在知道除数、商和余数的情况下,可以很方便的推算出被除数是多少。可以说linux内核对这块的设计真是做到了极致,不浪费一点点空间。

有了上面的认识,我们就知道select模型最大只能支持FD_SETSIZE个数的socket,而且socket的值也只能在FD_SETSIZE之内。如果socket()或accept()函数返回的socket值大于FD_SETSIZE,则select模型将出现错误——上面的计算将溢出。基于这种反向推理,我们可以放心大胆的使用0到FD_SETSIZE的值去当socket的值去计算。我看网上有很多select例子需要使用一个数组去维护接入的socket,如果在不考虑效率的前提下是不必要的。但是如果你追求极致的Select模型性能,还是建议使用一个数组去维护Socket,这样不至于出现大量的浪费操作。这块分析我们将在后文中给出。

这儿再多言一句,正是因为这种位操作,我们才需要在使用fd_set之前调用FD_ZERO去清空所有空间

# if __WORDSIZE == 64
#  define __FD_ZERO_STOS "stosq"
# else
#  define __FD_ZERO_STOS "stosl"
# endif# define __FD_ZERO(fdsp) \do {									      \int __d0, __d1;							      \__asm__ __volatile__ ("cld; rep; " __FD_ZERO_STOS			      \: "=c" (__d0), "=D" (__d1)			      \: "a" (0), "0" (sizeof (fd_set)		      \/ sizeof (__fd_mask)),	      \"1" (&__FDS_BITS (fdsp)[0])			      \: "memory");					      \} while (0)

如果socket不再需要监测,则我们使用__FD_CLR在fd_set中去除其对应的位

#define __FD_CLR(d, set) \((void) (__FDS_BITS (set)[__FD_ELT (d)] &= ~__FD_MASK (d)))

我们再来看下Select模型的处理能力。我们采用和《朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型》一文中相同的环境和压力,看下服务器的数据输出

再看下客户端的输出


        可见当前环境下,select模型的处理能力大概是每秒7000多连接。(和下一章介绍的Poll模型差距不大,而且如果使用数组维护Socket还可以提高性能)

相关文章:

微软开源NAS算法Petridish,提高神经网络迁移能力

作者 | Jesus Rodriguez译者 | Rachel编辑 | 夕颜出品 | AI科技大本营&#xff08;ID&#xff1a;rgznai100&#xff09; 【导读】神经架构搜索&#xff08;Neural architecture search, NAS&#xff09;是深度学习中最热门的趋势之一。NAS方法针对特定问题和数据…

[转]g++ 编译多个相关文件

三个文件&#xff0c;一个头文件&#xff0c;两个程序文件 /*d.h */#include <iostream>usingnamespacestd; classDataset { public: intgetdata(); }; /*d.cpp */#include "d.h"intDataset::getdata() { return1231; } /*out.cpp */#include <ios…

POJ--2391--Ombrophobic Bovines【分割点+Floyd+Dinic优化+二分法答案】最大网络流量

联系&#xff1a;http://poj.org/problem?id2391 题意&#xff1a;有f个草场&#xff0c;每一个草场当前有一定数目的牛在吃草&#xff0c;下雨时它能够让一定数量的牛在这里避雨&#xff0c;f个草场间有m条路连接&#xff0c;每头牛通过一条路从一点到还有一点有一定的时间花…

25年了,我总结出这些信息提取的经验教训

作者 | Ehud Reiter译者 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;【导读】近日&#xff0c;本文作者阿伯丁大学计算科学系教授 Ehud Reiter 及其带领的阅读小组读了一篇让他们印象深刻的论文——由 Ralph Grishman 发表的《信息提取 25 年》&#xff08…

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

在《朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型》中&#xff0c;我们分析了它只能支持1024个连接同时处理的原因。但是在有些需要同时处理更多连接的情况下&#xff0c;1024个连接往往是不够的&#xff0c;也就是不能够高并发。那么这个时候我们就可以采用…

flashcom中远程共享对象SharedObject的用法

觉得这篇文章比较好&#xff0c;转载回来。学习fcs也有差不多一个月了,感觉最有特色的东西还是SharedObject.SharedObject有不少东西,本地操作就不说了(相信很多人没接触fcs也用过);就说说远程共享对象吧.基本的应用流程是:my_nc new NetConnection(); my_nc.connect("rt…

Hive-1.2.0学习笔记(一)安装配置

鲁春利的工作笔记&#xff0c;好记性不如烂笔头下载hive&#xff1a;http://hive.apache.org/index.htmlHive是基于Hadoop的一个数据仓库工具&#xff0c;提供了SQL查询功能&#xff0c;能够将SQL语句解析成MapReduce任务对存储在HDFS上的数据进行处理。MySQ安装Hive有三种运行…

邮件安全隐患及其防范技术研究

邮件安全隐患及其防范技术研究<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />陈小兵【antian365.com】摘要电子邮件是Internet上使用最为频繁和广泛的服务&#xff0c;在给人们带来便利的同时&#xff0c;亦带来令人担忧的邮件…

必看!52篇深度强化学习收录论文汇总 | AAAI 2020

所有参与投票的 CSDN 用户都参加抽奖活动群内公布奖项&#xff0c;还有更多福利赠送来源 | 深度强化学习实验室&#xff08;ID:Deep-RL&#xff09;作者 | DeepRLAAAI 2020 共收到的有效论文投稿超过 8800 篇&#xff0c;其中 7737 篇论文进入评审环节&#xff0c;最终收录数量…

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

在阅读完《朴素、Select、Poll和Epoll网络编程模型实现和分析——Select模型》和《朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll模型》两篇文章后&#xff0c;我们发现一个问题&#xff0c;不管select函数还是poll函数都不够智能&#xff0c;它们只能告诉我们成功…

Scala 深入浅出实战经典 第88讲:Scala中使用For表达式实现map、flatMap、filter

高级函数 map,flatMap,filter用for循环的实现。package com.dt.scala.forexpressionobject For_Advanced {def main(args: Array[String]) {}def map[A, B](list: List[A], f: A > B): List[B] for(element <- list) yield f(element)def flatMap[A, B](list: List[A], f…

抛弃Python,我们为什么用Go编写机器学习架构?

所有参与投票的 CSDN 用户都参加抽奖活动群内公布奖项&#xff0c;还有更多福利赠送作者 | Caleb Kaiser译者 | 弯月&#xff0c;编辑 | 郭芮来源 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;如今&#xff0c;众所周知Python是机器学习项目中最流行的语言。尽管R、C…

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

经过之前四篇博文的介绍&#xff0c;可以大致清楚各种模型的编程步骤。现在我们来回顾下各种模型&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 模型编程步骤对比《朴素、Select、Poll和Epoll网络编程模型实现和分析——朴素模型》中介绍的是最基本的网络编程…

使用VM虚拟机的一点小技巧

今天想为朋友弄一个虚拟机系统文件&#xff0c;这样就可以直接拷贝过去&#xff0c;直接让他用了。哪成想电脑里的系统镜像文件不能用&#xff0c;也不知道是不是VM不支持&#xff0c;反正怎么着也引导不起来了。无奈只好用硬件光驱来装虚拟系统&#xff0c;把2003系统盘装入光…

翻译:AKKA笔记 - Actor消息 -1(二)

消息 我们只是让QuoteRequest到ActorRef去但是我们根本没见过消息类&#xff01; 它是这样的&#xff1a;&#xff08;一个最佳实践是把你的消息类包装在一个完整的对象里以利于更好的组织&#xff09; TeacherProtocol package me.rerun.akkanotes.messaging.protocolsobject …

远程安装oracle 10.2.1 for redhat 5.0 2.6.18-53.el5xen

远程安装oracle <?xml:namespace prefix st1 ns "urn:schemas-microsoft-com:office:smarttags" />10.2.1 for redhat 5.0 2.6.18-53.el5xen<?xml:namespace prefix o ns "urn:schemas-microsoft-com:office:office" />今天有个朋友打电…

伯克利新无监督强化学习方法:减少混沌所产生的突现行为

作者 | Glen Berseth译者 | Arvin编辑 | 夕颜出品 | AI科技大本营&#xff08;ID: rgznai100&#xff09;【导读】所有生命有机体都在环境中占据一席之地&#xff0c;使它们在周围不断增加的熵中可以保持相对可预测性。例如&#xff0c;人类竭尽全力保护自己免受意外袭击--我们…

朴素、Select、Poll和Epoll网络编程模型实现和分析——Poll、Epoll模型处理长连接性能比较

在《朴素、Select、Poll和Epoll网络编程模型实现和分析——模型比较》一文中&#xff0c;我们分析了各种模型在处理短连接时的能力。本文我们将讨论处理长连接时各个模型的性能。&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 我们可以想象下场景&#xff0c…

Topcoder SRM 663 DIV 1

ABBADiv1 题意&#xff1a; 规定两种操作&#xff0c;一种是在字符串的末尾添加A&#xff0c;另一种是在末尾添加B然后反转字符串。现在给你一个起始串&#xff0c;一个终点串&#xff0c;然后问你是否能够通过以上两种操作&#xff0c;从起始串变为终点串。 题解&#xff1a; …

跨平台PHP调试器设计及使用方法——立项

作为一个闲不住且希望一直能挑战自己的人&#xff0c;我总是在琢磨能做点什么。自从今年初开始接触PHP&#xff0c;我也总想能在这个领域内产生点贡献。那能做点什么呢&#xff1f;我经常看到很多phper说自己设计了一个什么框架&#xff0c;或者说自己搭建了一个什么系统。虽然…

机器推理文本+视觉,跨模态预训练新进展

作者 | 李根、段楠、周明来源 | 微软研究院AI头条&#xff08;ID:MSRAsia&#xff09;【导读】机器推理要求利用已有的知识和推断技术对未见过的输入信息作出判断&#xff0c;在自然语言处理领域中非常重要。本文将介绍微软亚洲研究院在跨模态预训练领域的研究进展。近年来&…

[LeetCode]:94:Binary Tree Inorder Traversal

题目&#xff1a; Given a binary tree, return the inorder traversal of its nodes values. For example:Given binary tree {1,#,2,3}, 1\2/3return [1,3,2]. 代码&#xff1a; public class Solution {public static ArrayList<Integer> listResult new ArrayList&l…

腾讯 AI 2019这一年

所有参与投票的 CSDN 用户都参加抽奖活动群内公布奖项&#xff0c;还有更多福利赠送近日&#xff0c;腾讯AI实验室总结了 2019 年其取得重大进展的两大研究方向&#xff0c;推动实现的行业应用以及前沿研究探索方面的成果。一、两大难题攻坚&#xff1a;通用人工智能与数字人用…

跨平台PHP调试器设计及使用方法——探索和设计

在《跨平台PHP调试器设计及使用方法——立项》一文中&#xff0c;我确定了使用xdebug作为调试器插件部分的基础组件。xdebug提供了一个远程调试的功能&#xff08;相关资料可以详见https://xdebug.org/docs/remote&#xff09;&#xff0c;我们这个项目便是基于这个功能实现的。…

Ubuntu下允许Root用户直接登录图形界面

ubuntu root是默认禁用了&#xff0c;不允许用root登陆&#xff0c;所以先要设置root密码。 执行&#xff1a;sudo passwd root 接着输入密码和root密码&#xff0c;重复密码。再重新启动就可以用root登陆。 另外&#xff0c;默认情况下是不允许用root帐号直接登陆图形界面的。…

携程App for Apple Watch探索

在Apple Watch发布之后&#xff0c;很多App都针对它设计了相应的版本。旅行作为与Apple Watch时间管理特性契合度较高的场景&#xff0c;同时携程旅行作为国内领先的OTA行业App&#xff0c;也成为了首批适配Apple Watch并荣登Apple官网和App Store推荐的应用之一。InfoQ就App f…

跨平台PHP调试器设计及使用方法——通信

首先引用《跨平台PHP调试器设计及使用方法——探索和设计》中的结构图&#xff08;转载请指明出于breaksoftware的csdn博客&#xff09; 本文要介绍的是我们逻辑和pydbgp通信的实现&#xff08;图中红框内内容&#xff09;。 设计通信之前&#xff0c;我需要先设计一种通信协议…

MVP模式的相关知识

MVP 是从经典的模式MVC演变而来&#xff0c;它们的基本思想有相通的地方&#xff1a;Controller/Presenter负责逻辑的处理&#xff0c;Model提供数据&#xff0c;View负责显示。作为一种新的模式&#xff0c;MVP与MVC有着一个重大的区别&#xff1a;在MVP中View并不直接使用Mod…

“数学不行,还能干点啥?”面试官+CTO:干啥都费劲!

关于数学与程序员的“暧昧”关系&#xff0c;先看看网友的看法&#xff1a;同时编程圈也流传着一个段子&#xff1a;一流程序员靠数学&#xff0c;二流程序员靠算法&#xff0c;末端程序员靠百度&#xff0c;低端看高端就是黑魔法。想一想&#xff0c;我们日常学习、求职、工作…

CentOS7 yum 源的配置与使用

YUM&#xff1a;Yellowdog Updater Modified Yum&#xff08;全称为 Yellow dog Updater, Modified&#xff09;是一个在Fedora和RedHat以及CentOS中的Shell前端软件包管理器。基于RPM包管理&#xff0c;能够从指定的服务器自动下载RPM包并且安装&#xff0c;可以自动处理依赖…