当前位置: 首页 > news >正文

漳州建设银行网站首页西安seo外包优化

漳州建设银行网站首页,西安seo外包优化,制作自己的网站 域名怎么弄,阿旗建设局举报网站Linux网络:多路转接 select一、系统调用二、echo server1、SelectServer类2、构造函数3、事件循环4、事件派发5、事件处理6、测试三、总结select是最早的多路转接方式,它允许用户同时监听多个文件的状态变化,当一个或多个文件状态发生变化&am…

Linux网络:多路转接 select

  • 一、系统调用
  • 二、echo server
    • 1、SelectServer类
    • 2、构造函数
    • 3、事件循环
    • 4、事件派发
    • 5、事件处理
    • 6、测试
  • 三、总结

select是最早的多路转接方式,它允许用户同时监听多个文件的状态变化,当一个或多个文件状态发生变化,select就会通知用户

基本的多路转接使用流程事件循环事件派发事件处理。

一、系统调用

Linux提供的系统调用函数就叫做select,需要包含头文件<sys/select.h>,函数声明如下:

在这里插入图片描述

此处引入了两个新的类型fd_setstruct timeval,它们是select所需的配置项。

  1. fd_set表示一个文件描述符的集合,定义如下:
typedef struct {unsigned long fds_bits[__FDSET_LONGS];
} __kernel_fd_set;

这个结构体内部,存储了一个unsigned long的数组,其实这是一张位图,位图的大小由__FDSET_LONGS这个宏决定。可以把多个文件的描述符fd添加到这张位图中,就可以形成一个文件描述符的集合。

  1. struct timeval表示一个时间,它用于设定select的返回时间,定义如下:
struct timeval{time_t tv_sec;susseconds_t tu_usec;
};

这个结构体的第一个变量tv_sec指定秒数,第二个变量tu_usec指定微秒数,两个数值结合,就得到一个指定的时间段

简单了解了这两个结构体的功能后,再看看select的五个参数

  1. nfds:监听的所有文件描述符中,最大的描述符的值 +1
  2. readfds:文件描述符集指针,可以为NULL,集合内包含要监听读事件的文件
  3. writefds:文件描述符集指针,可以为NULL,集合内存包含要监听写事件的文件
  4. exceptfds:文件描述符集指针,可以为NULL,集合内包含要监听异常事件的文件
  5. timeout:指向struct timeval的指针,表示这个select的超时时间

再解释一下三个文件描述符集合的事件:

  • 读事件:这个文件内有数据到来了,可以进行读取
  • 写事件:这个文件内有空位置了,可以进行写入
  • 异常事件:这个文件发生了异常,需要进行异常处理

在网络的IO模型中,最常用的就是readfds读事件,因为不确定网络报文何时到达,也就是文件内何时有数据,此时就可以让select代管。当select检测到某一个网络文件内有数据到来了,本质就是有网络报文到达,就会触发读事件,表示这个文件内有数据可读了。

文件描述符集合fd_set不能直接操作,操作系统提供了四个函数专门操作fd_set:

// 把 fd 从 fd_set 中移除
void FD_CLR(int fd, fd_set *set);// 把 fd 添加到 fd_set 中
void FD_SET(int fd, fd_set *set);// 检测 fd 是否在 fd_set 中
int  FD_ISSET(int fd, fd_set *set);// 把 fd_set 清空
void FD_ZERO(fd_set *set);

刚刚提到,fd_set本质是一个位图,它的大小由__FDSET_LONGS控制。而这个宏由操作系统维护,如果想要修改,需要重新编译内核,非常麻烦。所以select有一个重要的特性:

select能够同时监听的文件描述符fd是有上限的。

这其实是select的一个缺点,但是也不算很大的缺点。

另一个缺点才是最麻烦的:

每次select返回,三张fd_set集合全部都会被重新设置

在select中,三个fd_set传入的都是指针,这其实是一个输入输出型参数,输入时fd_set存储要监听事件的文件描述符,而返回时,三个fd_set都会被重新设置,表示这次调用select触发事件的文件描述符

  1. 每次select返回,都会清空没有触发事件的描述符,下一次调用select要把所有文件描述符重新设置一遍
  2. 用户等到select返回后,要遍历整个fd_set才知道哪些文件描述符就绪了

除此之外timeval也是一个输入输出型参数,它的返回值表示剩余的时间。一个select不一定等到timeval设置的事件结束才返回,有可能某些文件就绪就提前返回了,此时timeval会被设置成剩余时间。

最后select本身还有一个int的返回值,这表示本次select中,有多少个文件描述符就绪了

总结一下select的四个返回值:

  1. select函数返回值:表示本次调用select有几个文件就绪
  2. readfds:读事件就绪的文件描述符集合
  3. writefds:写事件就绪的文件描述符集合
  4. exceptfds:异常事件就绪的文件描述符集合
  5. timeout:返回时的剩余时间

二、echo server

接下来使用select系统调用,实现一个简单的echo server。

1、SelectServer类

class SelectServer
{// Select可以监听的最大文件描述符的数量const static int gnum = sizeof(fd_set) * 8;const static int gdefaultfd = -1;public:// 构造SelectServer(uint16_t port) : _port(port), _listensock(std::make_unique<TcpSocket>()){_listensock->BuildListenSocket(port); // 初始化的时候,通过调用Socket.hpp里面的函数:创建listenfd}// 初始化函数void InitServer(){}// 事件派发后:处理listensock的函数void Accepter(){}// 事件派发后:处理普通文件IO的函数void HandlerIO(int i){}// 事件派发函数:一定会存在大量的fd就绪,可能是普通sockfd,也可能是listensockfdvoid HandlerEvent(fd_set &rfds){// 事件派发 :遍历辅助数组里面的fd}// 打印测试函数void PrintDebug(){}// 循环!void Loop(){}// 析构~SelectServer(){}
private:uint16_t _port;std::unique_ptr<Socket> _listensock;// 1. select要正常工作,需要借助一个辅助数组,来保存所有合法fdint fd_array[gnum];
};

在SelectSerer类中,包含3个成员变量:

  • _port:链接该进程的端口号
  • _listensock:TCP的监听套接字的fd,用于监听到来的客户端连接
  • fd_array:select要正常工作,需要借助一个辅助数组,来保存所有合法fd

除此之外,还维护了一个常量gnum,这是select可以监听的套接字的最大数量。sizeof(fd_set)是fd_set的字节数,每个字节8 bit,所以gnum= sizeof(fd_set) * 8。

2、构造函数

SelectServer(uint16_t port) : _port(port), _listensock(std::make_unique<TcpSocket>()){_listensock->BuildListenSocket(port); // 初始化的时候,通过调用Socket.hpp里面的函数:创建listenfd}

构造函数接收一个端口号,表示这个服务监听的端口

开启服务:

  1. 先初始化把辅助数组里面的位图位置,设置为默认值,添加listensock到数组中
  2. 开启loop
void InitServer(){for (int i = 0; i < gnum; i++){fd_array[i] = gdefaultfd;}fd_array[0] = _listensock->Sockfd(); // 默认直接添加listensock到数组中}

这个函数用于进行死循环每一轮循环进行一次select的调用,监听所有套接字,并处理数据。

void Loop(){// 只要将新的fd加入到辅助数组里面就会由Select统一监管!}

事件派发:

void HandlerEvent(fd_set &rfds);

这个函数用于进行事件派发,接受一个fd_set,把这个集合内部的为1的描述符进行读取,并调用事件处理函数,完成业务处理

事件处理:

 Accepter();// 1. listensockfd HandlerIO(i);// 2、普通的fd的读写

套接字包含两种类型:listensockfd用于接收客户端连接,以及一般的客户端套接字sockfd用于完成echo server,这需要两个不同的函数进行处理。

3、事件循环

void Loop(){// 只要将新的fd加入到辅助数组里面就会由Select统一监管!while (true){// 1、文件描述符初始化fd_set rfds;FD_ZERO(&rfds);int max_fd = gdefaultfd;// 2. 合法的fd 添加到rfds集合中for (int i = 0; i < gnum; i++){if (fd_array[i] == gdefaultfd)continue;FD_SET(fd_array[i], &rfds);// 更新出最大的fd值if (max_fd < fd_array[i])max_fd = fd_array[i];}struct timeval timeout{3, 0};int n = ::select(_listensock->Sockfd() + 1, &rfds, nullptr, nullptr, &timeout);// Select的返回值:n>0就绪事件的个数,n=0,超时;n<0,出错switch (n){case 0:LOG(DEBUG, "time out,%d.%d\n", timeout.tv_sec, timeout.tv_usec);break;case -1:LOG(ERROR, "select error\n");default:LOG(INFO, "have event ready,n:%d\n", n);HandlerEvent(rfds); // 事件派发PrintDebug();       // 测试打印sleep(1);break;}}}

4、事件派发

// 一定会存在大量的fd就绪,可能是普通sockfd,也可能是listensockfdvoid HandlerEvent(fd_set &rfds){// 事件派发 :遍历辅助数组里面的fdfor (int i = 0; i < gnum; i++){if (fd_array[i] == gdefaultfd)continue;// 代码走到此时,FD一定是合法的,但是不一定就绪!if (FD_ISSET(_listensock->Sockfd(), &rfds)) // 就绪的文件fd可能有多个,所以FD_ISSET,就是用来判断,具体按一个就绪了{// 1. listensockfd if(_listensock->Sockfd()==fd_array[i]){Accepter();// 进行了封装}// 2、普通的fd的读写else {HandlerIO(i);}}}}

1. listensockfd2、普通的fd 的读写 分别进行了封装!

5、事件处理

  1. listensockfd 特殊处理:对网络监听套接字
void Accepter(){InetAddr addr;int sockfd = _listensock->Accepter(&addr);if (sockfd > 0){LOG(DEBUG, "get a new link,client info:%s:%d\n", addr.Ip().c_str(), addr.Port());// 事件处理bool flag = false;for (int pos = 1; pos < gnum; pos++){if (fd_array[pos] == gdefaultfd) // 说明该位置没有被设置!{flag = true;fd_array[pos] = sockfd;LOG(INFO, "add success %d to fd_array\n", sockfd);break;}}if (!flag){LOG(WARNING, "Server is full\n");::close(sockfd);}}}
  1. 对普通文件的读写处理!
void HandlerIO(int i){char buffer[1024];ssize_t n = ::recv(fd_array[i], buffer, sizeof(buffer) - 1, 0);if (n > 0){buffer[n] = 0;std::cout << "client say#" << buffer << std::endl;// 将客户端说的话,回显给服务器!std::string echo_str = "[server echo info]";echo_str += buffer;::send(fd_array[i], echo_str.c_str(), echo_str.size(), 0);}else if (n == 0) // 客户端啥也没说/退出{LOG(INFO, "client qiut...\n");// 关闭fd,让Select不再关心这个FD::close(fd_array[i]);fd_array[i] = gdefaultfd;}else{LOG(ERROR, "recv error\n");}return;}

6、测试

在这里插入图片描述
左侧是SelectServer,右侧是telnet客户端。可以看到,起初没有数据到来,一直触发timeout,当telnet发起连接,此时触发listenfd的事件,4 add to sockfds表示新的连接建立成功,并被select开始监听了。

随后用户发送hello、world都正常得到了响应,message: hello表示成功处理了客户端请求

三、总结

在刚才的代码中可以看出select的特性,每次循环都要重新设置所有的套接字。当select返回后要遍历所有的套接字,来判断哪个套接字可以进行读取了。所以select是一个比较麻烦的多路转接策略。

而当select返回后,还要依据不同的套接字类型,来进行不同的事件处理。这也就是基本的多路转接使用流程事件循环事件派发事件处理

http://www.hlhnt8889177.com/news/174.html

相关文章:

  • 网站怎么发布今日热点新闻头条国内
  • 服装网站建设规划方案南京seo网络优化公司
  • 美容美发网站源码百度账号人工客服电话
  • 用ftp上传wordpress后清远seo
  • 长沙企业建站系统搜索引擎的四个组成部分及作用
  • 典型的四大综合门户网站网络推广收费价目表
  • 模版网站可以做seo吗可以全部免费观看的软件
  • 免费下载网站设计方案抖音seo培训
  • 营销网站怎么做合适2023年8月新冠又来了
  • 网站开发形成收入怎么做帐学前端去哪个培训机构
  • 网站建设外包多少钱系统优化软件排行榜
  • 作词做曲网站十大免费网站推广平台有哪些
  • 要找做冲压件的厂去哪个网站找app优化推广
  • dnspod网站备案不关站优化网站的方法
  • 怎样做直播网站app如何推广自己的店铺
  • 90设计网素材下载短视频seo排名
  • 南通有哪些礼品公司网络seo啥意思
  • 专业建站开发谷歌关键词
  • 范例网站怎么做网络营销推广及优化方案
  • 开发公司修路的费用免费seo网站
  • 大连网站制作案例百度帐号注册
  • 武汉 网站建设chrome浏览器下载安卓手机
  • WordPress电影网南京seo公司哪家
  • 如何wix 做 网站外贸营销平台
  • 中国搜索引擎网站排名免费推广平台有哪些
  • 珠海做网站的公司有哪些上海牛巨仁seo
  • 深圳网站建设开发沈阳网站推广优化
  • 程序员 做网站 微信公众号 赚钱成都网站快速排名优化
  • 好看的企业门户网站媒体:多地新增感染趋势回落
  • 绿色食品网站源码怎么创建网页链接