当前位置: 首页 > 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.lryc.cn/news/609187.html

相关文章:

  • Unity JobSystem 与 BurstCompiler 资料
  • 2025.8.3
  • webrtv弱网-QualityScalerResource 源码分析及算法原理
  • 【大模型实战】向量数据库实战 - Chroma Milvus
  • Linux mount挂载选项详解(重点关注nosuid)
  • ESP32开发问题汇总
  • ZStack Cloud 5.3.40正式发布
  • 第15届蓝桥杯Scratch图形化国赛初/中级组2024年9月7日真题
  • Product Hunt 每日热榜 | 2025-08-02
  • 01数据结构-时间复杂度和空间复杂度
  • Petalinux 23.2 构建过程中常见下载错误及解决方法总结
  • ORA-12514:TNS: 监听程序当前无法识别连接描述符中请求的服务
  • 小白学OpenCV系列2-理解图像
  • 使用纯Docker命令搭建多服务环境(Linux版)
  • Web 开发 11
  • 腾讯人脸识别
  • lumerical——锥形波导偏振转换
  • 大白话讲解MCP
  • 机器学习第四课之决策树
  • Android 之 蓝牙通信(2.0 经典)
  • Kaggle 竞赛入门指南
  • ELECTRICAL靶机复现练习笔记
  • C++中多线程和互斥锁的基本使用
  • 【数据结构】二叉树的顺序结构实现
  • 15_01_opencv_形态学滤波
  • 35.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--数据缓存
  • Android 之 RxJava2
  • Kali基础知识点【1】
  • 基于图像识别与分类的中国蛇类识别系统
  • gitee使用教程