c++socket的select函数以及多路复用

c++socket的select函数以及多路复用

  • 1.select函数原型
    • 使用示例及解释
  • 2.select()函数返回值
  • 3.select()函数与socket阻塞的关系与原因
  • 4.select函数的意义
  • 5.深入理解select模型
    • (1)特点
    • (2)带外数据
    • (2)select函数相关的常见的几个宏
    • (3)select使用范例及结论
  • 6.多路复用及代码示例

1.select函数原型

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

参数说明:
nfds:需要监视的最大文件描述符值加1。
readfds、writefds、exceptfds:分别表示可读、可写和异常事件的文件描述符集合。这些集合是用fd_set结构体表示的。
timeout:指定超时时间,如果为NULL,则select函数会一直阻塞,直到有事件发生。如果指定为0,则select函数会立即返回,用于轮询文件描述符的状态。
select函数的返回值是就绪文件描述符的个数。如果返回0,表示超时;如果返回-1,表示出错。
使用select函数的步骤如下:
(1)准备需要监视的文件描述符集合,并初始化它们。
(2)调用select函数,进行文件描述符的监视。
(3)检查select函数的返回值,判断哪些文件描述符就绪。
(4)对就绪的文件描述符进行相应的操作

使用示例及解释

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
int main() {
int sockfd;
fd_set readfds;
struct timeval timeout;
// 创建socket
sockfd = socket(AF_INET, SOCK_STREAM, 0);// 设置需要监视的文件描述符集合
FD_ZERO(&readfds);
FD_SET(sockfd, &readfds);// 设置超时时间
timeout.tv_sec = 5;
timeout.tv_usec = 0;// 监视文件描述符
int ret = select(sockfd + 1, &readfds, NULL, NULL, &timeout);
if (ret == -1) {std::cout << "select error" << std::endl;
} else if (ret == 0) {std::cout << "timeout" << std::endl;
} else {if (FD_ISSET(sockfd, &readfds)) {std::cout << "socket is readable" << std::endl;}
}return 0;

解释:
首先创建了一个socket,并将其添加到readfds集合中。然后设置了一个超时时间为5秒,调用select函数进行文件描述符的监视。如果select函数返回0,表示超时;如果返回-1,表示出错;如果返回大于0,表示有文件描述符就绪。通过FD_ISSET宏来判断sockfd是否就绪,如果就绪则输出"socket is readable"。
需要注意的是,在使用select函数之前,需要将文件描述符集合进行初始化,并将需要监视的文件描述符添加到对应的集合中。另外,如果监视的文件描述符集合很大,可以使用FD_SETSIZE常量来表示文件描述符集合的大小。

2.select()函数返回值

select的返回值与recv函数的返回值十分类似,都分>0,=0,<0三种情况。
(1) >0:有事件发生

(2)=0:timeout,超时

此时需要先FD_ZERO(),FD_SET();再select。
原因:
要监视的文件描述符在位图中被置1,当有事件发生时,文件描述符仍为1,但没有事件发生的文件描述符会被置0,所以,要想监视所有的文件描述符,第2此循环需要重新将所有的文件描述符进行FD_SET();置1设置。

(3)<0:出错。
在出错的情况下,当错误为EINTR,认为连接是正常的,继续接收
if(errno == EINTR)continue;
报此错误需要继续select;不需要FD_ZERO(),FD_SET();
select函数没有(errno == EAGAIN)||errno == EWOULDBLOCK)错误;
这两种错误是recv和send是会出现的错误

3.select()函数与socket阻塞的关系与原因

关系:
select()与socket是否阻塞没有任何关系,也就是说,无论socket是阻塞状态还是非阻塞态都可以与select进行搭配。
原因:
当某个设备是阻塞的时候,相当于处于sleep的状态,即:让出CPU,
但当设备处于非阻塞态时,会不断的轮询,占用CPU,故默认的是阻塞态。

4.select函数的意义

1)select函数是多路IO复用,故当有多路IO的时候,会发挥优势:
2)当某一路IO处于阻塞态时,select监视到其他的IO有读或写,就可以让其他路的IO读写;如果没有select,当有一路IO阻塞,其他的IO即使可以读写,也无法进行。

5.深入理解select模型

(1)特点

取fd_set长度为1字节,fd_set中的每一bit可以对应一个文件描述符fd。则1字节长的fd_set最大可以对应8个fd。
(1)执行fd_set set; FD_ZERO(&set); 则set用位表示是0000,0000。
(2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1)
(3)若再加入fd=2,fd=1,则set变为0001,0011
(4)执行select(6,&set,0,0,0)阻塞等待
(5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。
基于上面的讨论,可以轻松得出select模型的特点:
(1)可监控的文件描述符个数取决与sizeof(fd_set)的值。我这边服务器上sizeof(fd_set)=512,每bit表示一个文件描述符,则我服务器上支持的最大文件描述符是512*8=4096。据说可调,另有说虽然可调,但调整上限受于编译内核时的变量值。
(2)将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。
(3)可见select模型必须在select前循环加fd,取maxfd,select返回后利用FD_ISSET判断是否有事件发生。

(2)带外数据

带外数据(out—of—band data),有时也称为加速数据(expedited data),
是指连接双方中的一方发生重要事情,想要迅速地通知对方。
这种通知在已经排队等待发送的任何“普通”(有时称为“带内”)数据之前发送。
带外数据设计为比普通数据有更高的优先级。
带外数据是映射到现有的连接中的,而不是在客户机和服务器间再用一个连接。

(2)select函数相关的常见的几个宏

#include <sys/select.h>
int FD_ZERO(int fd, fd_set *fdset);   //一个 fd_set类型变量的所有位都设为 0
int FD_CLR(int fd, fd_set *fdset);  //清除某个位时可以使用
int FD_SET(int fd, fd_set *fdset);  //设置变量的某个位置位
int FD_ISSET(int fd, fd_set *fdset); //测试某个位是否被置位

(3)select使用范例及结论

当声明了一个文件描述符集后,必须用FD_ZERO将所有位置零。之后将我们所感兴趣的描述符所对应的位置位,操作如下:

fd_set rset;
int fd;
FD_ZERO(&rset);
FD_SET(fd, &rset);
FD_SET(stdin, &rset);

然后调用select函数,拥塞等待文件描述符事件的到来;如果超过设定的时间,则不再等待,继续往下执行

select(fd+1, &rset, NULL, NULL,NULL);
select返回后,用FD_ISSET测试给定位是否置位:
if(FD_ISSET(fd, &rset)
{
...
//do something
}

结论:
select模型的关键是使用一种有序的方式,对多个套接字进行统一管理与调度 。用户首先将需要进行IO操作的socket添加到select中,然后阻塞等待select系统调用返回。当数据到达时,socket被激活,select函数返回。用户线程正式发起read请求,读取数据并继续执行。
从流程上来看,使用select函数进行IO请求和同步阻塞模型没有太大的区别,甚至还多了添加监视socket,以及调用select函数的额外操作,效率更差。但是,使用select以后最大的优势是用户可以在一个线程内同时处理多个socket的IO请求。用户可以注册多个socket,然后不断地调用select读取被激活的socket,即可达到在同一个线程内同时处理多个IO请求的目的。而在同步阻塞模型中,必须通过多线程的方式才能达到这个目的。

6.多路复用及代码示例

C++中使用多路复用技术来处理socket可以提高程序的性能和效率。多路复用允许一个进程同时监听多个socket,当任何一个socket准备好进行读写操作时,进程就可以对其进行相应的处理。
在C++中,可以使用select、poll或者epoll等系统调用来实现多路复用。这些系统调用可以监视一组socket的状态变化,包括可读、可写、异常等事件。
代码示例:

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstdlib>
#include <cstring>int main() {// 创建socketint serverSocket = socket(AF_INET, SOCK_STREAM, 0);if (serverSocket == -1) {std::cerr << "Failed to create socket" << std::endl;return -1;}// 设置socket地址sockaddr_in serverAddress{};serverAddress.sin_family = AF_INET;serverAddress.sin_port = htons(8080);serverAddress.sin_addr.s_addr = INADDR_ANY;// 绑定socket地址if (bind(serverSocket, (struct sockaddr*)&serverAddress, sizeof(serverAddress)) == -1) {std::cerr << "Failed to bind socket" << std::endl;close(serverSocket);return -1;}// 监听socketif (listen(serverSocket, SOMAXCONN) == -1) {std::cerr << "Failed to listen on socket" << std::endl;close(serverSocket);return -1;}// 设置文件描述符集合fd_set readSet;FD_ZERO(&readSet);FD_SET(serverSocket, &readSet);// 循环等待socket事件while (true) {// 复制文件描述符集合fd_set tempSet = readSet;// 调用select等待事件发生if (select(FD_SETSIZE, &tempSet, nullptr, nullptr, nullptr) == -1) {std::cerr << "Failed to select" << std::endl;close(serverSocket);return -1;}// 遍历文件描述符集合for (int fd = 0; fd < FD_SETSIZE; ++fd) {// 检查是否有事件发生if (FD_ISSET(fd, &tempSet)) {// 如果是serverSocket有事件发生,表示有新的连接请求if (fd == serverSocket) {sockaddr_in clientAddress{};socklen_t clientAddressSize = sizeof(clientAddress);// 接受新的连接int clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddress, &clientAddressSize);if (clientSocket == -1) {std::cerr << "Failed to accept connection" << std::endl;close(serverSocket);return -1;}std::cout << "New connection accepted" << std::endl;// 将新的连接加入文件描述符集合FD_SET(clientSocket, &readSet);}// 否则,表示有数据可读else {// 读取数据char buffer[1024];memset(buffer, 0, sizeof(buffer));ssize_t bytesRead = recv(fd, buffer, sizeof(buffer) - 1, 0);if (bytesRead == -1) {std::cout << "Failed to receive data" << std::endl;close(serverSocket);return -1;}// 处理数据if (bytesRead == 0) {std::cout << "Connection closed" << std::endl;close(fd);FD_CLR(fd, &readSet);} else {std::cout << "Received data: " << buffer << std::endl;}}}}}// 关闭socketclose(serverSocket);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/181007.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

和鲸全程支持:第二届粤港澳大湾区(黄埔)国际算法算例大赛初赛赛程圆满收官!

随着新一轮科技革命与产业变革的加速演进&#xff0c;算法&#xff0c;作为一种战略性的科技、生产要素&#xff0c;已成为推动数字技术与实体经济深度融合的核心支撑。为助力地区大数据与人工智能算法的生态体系建设、赋能社会经济的高质量发展&#xff0c;琶洲实验室&#xf…

如何与LEONI建立EDI连接?

莱尼LEONI是一家为汽车及其他行业提供能源数据管理产品、解决方案及服务的全球供应商。供应链范围从研发生产标准化电缆、特种电缆和数据电缆到高度复杂的布线系统和相关组件。本文将介绍如何与莱尼LEONI建立EDI连接。 什么是EDI&#xff1f; EDI全称Electronic Data Interch…

C# WPF上位机开发(乘法计算小软件)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 上面一篇文章&#xff0c;我们简单了解了怎么用xaml来设计界面。和传统的c# form不一样&#xff0c;它除了可以通过拖拽的方法来实现界面的编写之外…

各种排序算法

1.Arrays.sort---默认升序 //降序排序 Integer a[] {6,9,9}; Arrays.sort(a, Collections.reverseOrder()); for (int i 0; i < a.length; i) {System.out.print(a[i]); } 2.直接插入排序 ① 从第一个元素开始&#xff0c;该元素可以认为已经被排序 ② 取出下一个元素&…

基于SSM的云鑫曦科技办公自动化管理系统设计与实现

基于SSM的云鑫曦科技办公自动化管理系统设计与实现 摘 要: 随着时代的发展&#xff0c;单位办公方式逐渐从传统的线下纸张办公转向了使用个人pc的线上办公&#xff0c;办公效率低下的传统纸质化办公时代的淘汰&#xff0c;转型到信息化办公时代&#xff0c;面对当今数据逐渐膨…

python每日一题——13最大子数组和

题目 用python做题&#xff0c;给出详细的解题思路和代码注释&#xff1a;给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。子数组是数组中的一个连续部分。 示例 1&#xf…

灰度发布专题---5、API网关灰度发布

API网关灰度发布 前面说到Dubbo灰度发布&#xff0c;那网关代理层如何实现灰度发布呢&#xff0c;在网关层实现灰度发布&#xff0c;我们可以采用2种方式实现&#xff0c;分别是权重和灰度规则配置。在这之前我们先了解下Gateway的源码&#xff0c;更利于后面灰度分析。 Gate…

笔记-PC端wireshark采集FPGA数据的操作

wireshark采集FPGA的数据 目录 一、准备工作二、操作步骤 一、准备工作 1、软件&#xff1a;wireshark 2、平台&#xff1a;PC&#xff08;本人是win11&#xff09;、带有以太网功能的zynq平台 3、网线: 用网线连接zynq板子和PC的以太口端口 二、操作步骤 1、打开任务管理器…

android开发:安卓13Wifi和热点查看与设置功能

近日对安卓热点功能做了一些技术验证&#xff0c;目的是想利用手机开热点给设备做初始化&#xff0c;用的是安卓13&#xff0c;简言之&#xff1a; 热点设置功能不可用&#xff0c;不可设置SSID和密码&#xff0c;不可程序控制开启关闭&#xff0c;网上的代码统统都过时了Loca…

JVM执行引擎

目录 &#xff08;一&#xff09;执行引擎概述 &#xff08;二&#xff09;Java代码编译和执行过程 &#xff08;三&#xff09;机器码&#xff0c;指令&#xff0c;汇编语言&#xff0c;字节码 1、机器码 2、指令 3、指令集 4、汇编 5、字节码 &#xff08;四&#x…

在Visual Studio Code中安装加速TypeScript程序开发的插件

在Visual Studio Code中安装加速TypeScript程序开发的插件 Install Extensions on Visual Studio Code for TypeScript Application Development By Jackson 2023-11-28 众所周知&#xff0c;微软的Visual Studio Code是一款轻量级、功能强大的集成开发环境。它支持各种编程语…

linux CentOS MobaXterm 通过X11 Forwarding 在本地开启图形可视化窗口

第一步 操作系统安装图形界面 X11 Forwarding dnf install xorg-x11-xauth xorg-x11-fonts-* xorg-x11-font-utils xorg-x11-fonts-Type1 xclock第二步 修改参数&#xff0c;启用X11 Forwarding vim /etc/ssh/sshd_config修改参数X11Forwarding yes和X11UseLocalhost no #Al…

C/C++---------------LeetCode第35. 搜索插入位置

插入的位置 题目及要求二分查找在main内使用 题目及要求 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 示例 1: …

AIGC系列之:GroundingDNIO原理解读及在Stable Diffusion中使用

目录 1.前言 2.方法概括 3.算法介绍 3.1图像-文本特征提取与增强 3.2基于文本引导的目标检测 3.3跨模态解码器 3.4文本prompt特征提取 4.应用场景 4.1结合生成模型完成目标区域生成 4.2结合stable diffusion完成图像编辑 4.3结合分割模型完成任意图像分割 1.前言 …

Docker容器常用命令

文章目录 启动类命令帮助类命令镜像命令列出本地主机上的镜像在远程仓库中搜索镜像下载镜像保存镜像加载 tar 包为镜像查看占据的空间删除镜像 虚悬镜像命令自动补全新建启动容器启动交互式容器启动守护式容器 列出正在运行的容器容器其他启停操作启动已经停止的容器重启容器停…

c++|类与对象(中)

目录 一、类的6个默认成员函数 二、构造函数 2.1概念 2.2七大特性 三、析构函数 3.1概念 3.2特性 四、拷贝构造函数 4.1概念 4.2特性 五、赋值运算符重载 5.1运算符重载 5.2赋值运算符重载 5.3前置和后置重载 六、const成员函数 七、取地址及const取地址操作符重…

【算法】算法题-20231129

这里写目录标题 一、15. 三数之和二、205. 同构字符串三、383. 赎金信 一、15. 三数之和 提示 中等 6.5K 相关企业 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] …

Redis中的缓存穿透、雪崩、击穿(详细)

目录 一、概念 1. 缓存穿透&#xff08;Cache Penetration&#xff09; 解决方案&#xff1a; 2. 缓存雪崩&#xff08;Cache Avalanche&#xff09; 解决方案&#xff1a; 3. 缓存击穿&#xff08;Cache Breakdown&#xff09; 解决方案&#xff1a; 二、三者出现的根本原…

中英双语大模型ChatGLM论文阅读笔记

论文传送门&#xff1a; [1] GLM: General Language Model Pretraining with Autoregressive Blank Infilling [2] Glm-130b: An open bilingual pre-trained model Github链接&#xff1a; THUDM/ChatGLM-6B 目录 笔记AbstractIntroductionThe design choices of GLM-130BThe …

Mac苹果视频剪辑:Final Cut Pro Mac

Final Cut Pro是一款由Apple公司开发的专业视频非线性编辑软件&#xff0c;是业界著名的视频剪辑软件之一。它最初发布于1999年&#xff0c;是Mac电脑上的一款独占软件。Final Cut Pro具有先进的剪辑工具、丰富的特效和颜色分级、音频处理等功能&#xff0c;使得用户可以轻松地…