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

Linux网络编程 --- 多路转接select

多路转接select

  • 一、系统调用接口介绍
    • 二、接口核心部分
      • 三、接口使用案例
        • 四、select的特点、缺点

在这里插入图片描述

一、系统调用接口介绍

在这里插入图片描述

二、接口核心部分

在这里插入图片描述

三、接口使用案例

#pragma once
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <memory>
#include "Socket.hpp"
using namespace SocketModel;const int fdsize = 1024; // select所能同时关注的最大文件描述符数
const int defaultfd = -1;class SelectServer
{
public:SelectServer(int port) : _listensocketfd(std::make_unique<TCPSocket>()){_listensocketfd->BuildTCPSocket(port); // 创建listen套接字+绑定端口号memset(_fd_array, defaultfd, sizeof(_fd_array));_fd_array[0] = _listensocketfd->FD(); // 设置默认第一个需要被select关注的文件描述符是监听套接字fd}~SelectServer(){}public:void Start(){_isrunning = true;while (_isrunning){// 之前的写法://_commusocketfd=_listensocketfd->Accept() //获取监听到的链接信息,返回用于通信的文件描述符// 但是://_listensocket本质也是一个文件描述符,进程怎么知道该fd上有新连接到了呢?// 而 accept本身就是阻塞的,如果直接调用,进程就会阻塞在这里// 所以,我们可以使用select,让操作系统帮我们关注该_listensocketfd// 如果有新链接到来,就意味着有事件就绪!!!!!!!!// 1.定义fd_setfd_set rfds;// 2.将_listensocketfd设置进该rfds// int listenfd = _listensocketfd->FD();// FD_SET(listenfd, &rfds);// 2.将_fd_array存的文件描述符全都让select帮我们关注int maxfd = -1;for (int i = 0; i < fdsize; i++){if (_fd_array[i] != defaultfd){FD_SET(_fd_array[i], &rfds);maxfd = std::max(maxfd, _fd_array[i]); // 找到最大的fd号}}// 3.设置超时时间struct timeval _tm = {0, 0};PrintFD();// 4.上面设置rfds,是在用户空间进行的,如果要让select帮我们关注这些文件描述符,必须要设置进内核:int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);switch (n){case -1:LOG(LogLevel::ERROR) << "select error";break;case 0: // 超时了LOG(LogLevel::INFO) << "time out";break;default: // 有n个fd的事件就绪LOG(LogLevel::INFO) << "event-ready , 就绪事件个数n= " << n;HandlerEvent(&rfds);break;}}_isrunning = false;}void HandlerEvent(fd_set *rfds){// 并不知道是哪些fd就绪,所以需要遍历:for (int i = 0; i < fdsize; i++){if (_fd_array[i] == defaultfd) // 不合法fdcontinue;// FD_ISSET是判断某个fd是否在rfds里面,如果在,说明它的"读"事件就绪了if (FD_ISSET(_fd_array[i], rfds)) //{if (_fd_array[i] == _listensocketfd->FD()) // 1.1 有新链接到来!{Accepter();}else // 1.2 一般的读数据事件{ReadEvent(_fd_array[i], i);}}// else if(FD_ISSET(_fd_array[i], wfd)) //2. 判断该文件描述符的"写"事件是否就绪了//.....}}void Accepter(){InetAddr _client;// 注意:自此,在这里accept就不会再阻塞了!!!!!!!!int commu_fd = _listensocketfd->Accept(_client);// accept得到一个新的用于通信的fd// 1.找到_fd_array的一个空缺的位置int pos = 0;for (; pos < fdsize; pos++){if (_fd_array[pos] == defaultfd)break;}if (pos < fdsize){// 2.将该新的用于通信的fd存入_fd_array中,让select帮我关注这个fd的事件是否就绪LOG(LogLevel::INFO) << "get a new commufd, fd= " << commu_fd;_fd_array[pos] = commu_fd;}else //_fd_array满了(服务器能同时服务的用户达到上限!){LOG(LogLevel::INFO) << "_fd_array full.....";close(commu_fd); // 防止文件描述符泄漏,要记得关闭掉它}}void ReadEvent(int rfd, int index) // 该文件描述符的读事件就绪{char buffer[1024];// 注意:自此,在这里recv不会再发生阻塞!!!!!// 注意:这里是有bug的,因为tcp协议面向字节流,并不知道读的数据是否完整等...这里不做处理ssize_t n = recv(rfd, buffer, sizeof(buffer) - 1, 0);if (n > 0){buffer[n] = 0;LOG(LogLevel::INFO) << "client say@ : " << buffer;}else if (n == 0) // 客户端退出了{LOG(LogLevel::INFO) << "client quit...";// 注意:记得释放与客户端通信的文件描述符! ! !close(rfd);// 并且将该fd从_fd_array去除掉_fd_array[index] = defaultfd;}else // 读出错{LOG(LogLevel::ERROR) << "recv error!";}}void PrintFD(){for (int i = 0; i < fdsize; i++){if (_fd_array[i] != defaultfd){std::cout << _fd_array[i] << " ";}}std::cout << std::endl;}void Stop(){_isrunning = false;}private:std::unique_ptr<BaseSocket> _listensocketfd; // 用于监听的套接字// 注意: 因为select返回时,会修改我们传进去的rfds/wfds来告诉用户,哪些文件描述符准备好了//       这就导致: 下一次select时, rfds和wfds的内容就不是我们之前设置给select的了// 所以:设置rfds或wfds必须在while(_ifrunning)循环内部,即,每次让select帮我们关注哪些//       fd是否就绪前,都要重新设置rfds或wfds。// 并且需要借助一个辅助数组_fd_array来记录那些需要被select关注的fd!!!!!//(用数组管理比较简单合适,毕竟select本身能同时关注的fd就有限)int _fd_array[fdsize]; // 用来存需要被select关注的文件描述符bool _isrunning = false;
};

在这里插入图片描述
在这里插入图片描述

四、select的特点、缺点

在这里插入图片描述

http://www.dtcms.com/a/313123.html

相关文章:

  • 推送本地项目到Gitee远程仓库
  • Selenium Web 自动化
  • 优选算法 力扣 202.快乐数 快慢双指针 解决带环问题 C++解题思路 每日一题
  • ThinkPHP5x,struts2等框架靶场复现
  • Coin Combinations II(Dynamic Programming)
  • LLM - AI大模型应用集成协议三件套 MCP、A2A与AG-UI
  • 用 Eland 在 Elasticsearch Serverless 部署 Learning-to-Rank 排序模型
  • 数据,正在成为AI大模型最后的护城河
  • leetcode 2106. 摘水果 困难
  • Rust 同步方式访问 REST API 的完整指南
  • 道格拉斯-普克算法 - 把一堆复杂的线条变得简单,同时尽量保持原来的样子
  • python---赋值、浅拷贝、深拷贝
  • 【C 学习】03-你的第一个C程序
  • 上位机知识篇---脚本文件
  • Linux环境下使用Docker搭建多服务环境
  • Corrosion2靶场
  • xxljob总结
  • Obsidian结合CI/CD实现自动发布
  • 1、docker容器命令 | 生命周期管理
  • NX969NX972美光固态闪存NX975NX977
  • python 12 install jupyter时zmq.h或libzmq报错处理
  • MVCC:数据库事务隔离的 “时空魔法”
  • nvm切换本地nodejs环境
  • node中shapefile字符集判断
  • Sklearn 机器学习 数据聚类 KMeans实现聚类
  • wav音频格式中,ACM波形、A/mu-Law Wave、Windows PCM、Microsoft ADPCM的区别
  • 《使用Qt Quick从零构建AI螺丝瑕疵检测系统》——9. 接入真实硬件:驱动USB摄像头
  • LeetCode 分类刷题:2824. 统计和小于目标的下标对数目
  • Go语言--语法基础7--函数定义与调用--自定义函数
  • Go语言实战案例:TCP服务器与客户端通信