网络io与io多路复用select/poll/epoll

news2024/12/28 5:19:44

一、网络IO请求

网络I/O请求是指在计算机网络中,向其他主机或服务器发送请求或接收响应的操作。这些请求可以包括获取网页、下载文件、发送电子邮件等。网络I/O请求需要使用合适的协议和通信方式来进行数据传输,例如HTTP、FTP、SMTP等。

要完成一个完整的 TCP/IP 网络通信过程,需要使用一系列函数来实现。这些函数包括 bind、listen、accept 和 recv/send 等。下面是它们的配合流程:

  1. 创建套接字(socket):使用 socket 函数创建一个套接字,指定协议族和套接字类型。
  2. 绑定地址(bind):将本地地址绑定到套接字上,使得客户端可以通过该地址访问服务器。
  3. 监听连接请求(listen):将套接字设置为监听状态,并指定最大等待连接数(backlog)。
  4. 接受连接请求(accept):当有客户端发起连接请求时,使用 accept 函数创建新的套接字用于与客户端进行通信。
  5. 读写数据(recv/send):使用新创建的套接字进行数据传输,包括从客户端读取数据和向客户端发送数据。
  6. 关闭连接(close):在通信结束后,需要使用 close 函数关闭套接字以释放资源。

对于第4步的请求,如果向下面方式处理,则只能接受一个客户端的请求。注意,如果把accept放在while循环里,也不能解决多客户端请求,反而会发生阻塞。

	int clientfd=accept(sockfd,(struct sockaddr*)&clientaddr,&len); //调用 accept() 函数后,它会一直阻塞等待直到有新的客户端连接请求到达为止。

	printf("accept\n");

	while (1){
		char buffer[BUFFER_LEN]={0};
		int ret=recv(clientfd,buffer,BUFFER_LEN,0);
		printf("ret: %d,buffer:%s\n", ret,buffer);
		send(clientfd,buffer,ret,0);
	}

因此,若要处理多客户端的情况,可以采用以下方法

  • 一请求一线程
  • select
  • poll
  • epoll

二、一请求一线程

如下图所示,一请求一线程的方式,确实可以解决多客户端连接和收发信息的情况。但是,实际业务中,面对数以万计的客户端,如果每个开辟一个线程,将会带来很大的消耗。好比你开一家餐厅,如果来一个顾客就要安排一个服务员,那如果你客流量上千,那不得雇佣一千个服务员!!因此,解决思路就是如果让一个服务员服务多个顾客。
在这里插入图片描述
在这里插入图片描述

三、IO多路复用——select的通俗理解

I/O多路复用是指一种机制,它允许单个进程可以监视多个文件描述符(通常是套接字),并在这些文件描述符中的任何一个变为可读或可写时立即进行相应的处理。这样就可以避免使用多线程/多进程方式来实现高并发。

1、select函数

select函数是一个I/O多路复用函数,用于同时监听多个文件描述符上的可读、可写、异常等事件。它可以让程序在单线程下同时处理多个I/O操作,提高程序的并发性能。
select函数的原型为:
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
参数说明:

  • nfds:需要监听的最大文件描述符值加1。
  • readfds:读事件集合。
  • writefds:写事件集合。
  • exceptfds:异常事件集合。
  • timeout:超时时间,当所有文件描述符都没有事件时,select函数会阻塞等待事件到来,如果超过了超时时间还没有事件到来,则返回0。

select函数返回值:

  • 大于0表示有文件描述符就绪;
  • 等于0表示超时;
  • 小于0表示出错。

2、accpet函数

accept()函数用于接受一个已经建立的连接,并返回一个新的套接字描述符,以便与该连接进行通信。该函数的原型如下:
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
参数解释如下:

  • sockfd:需要等待连接的套接字文件描述符。
  • addr:指向存放远程主机地址信息的缓冲区。
  • addrlen:远程主机地址信息长度。

返回值是新的套接字文件描述符,如果失败则返回-1。
该函数会一直阻塞,直到有客户端请求连接。一旦有新的连接请求,它将创建一个新的套接字,并使用该套接字来与客户端进行通信,而原始套接字则继续监听其他连接请求。

3、recv函数

recv()函数用于从已连接的套接字中接收数据。该函数的原型如下:
ssize_t recv(int sockfd, void *buf, size_t len, int flags);
参数解释如下:

  • sockfd:需要接收数据的套接字文件描述符。
  • buf:用于存储接收到数据的缓冲区地址。
  • len:缓冲区长度。
  • flags:调用方式标志,通常设置为0。

返回值是实际读取到的字节数,如果返回0表示连接被关闭,如果返回-1表示发生错误。
该函数通过网络读取指定长度(len)的数据,并将其存储在指定地址(buf)所指向的缓冲区中。它会一直阻塞等待直到有足够的数据可供读取或者出错。
在这里插入图片描述

四、IO多路复用——poll

需要包含头文件#include <poll.h>
poll函数是一个系统调用,用于等待多个文件描述符上的事件。它与select函数类似,但提供了更好的性能和可扩展性。
在使用poll函数时,需要创建一个pollfd结构体数组来指定要监视的文件描述符及其感兴趣的事件类型。每个结构体包含以下字段:

  • fd:表示要监视的文件描述符
  • events:表示所关注的事件类型(如POLLIN表示可读事件)
  • revents:返回时表示发生了哪些事件

poll函数的参数包括一个指向pollfd结构体数组的指针、数组中元素的数量以及超时时间。具体来说,它的定义如下:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

  • fds:指向pollfd结构体数组的指针,用于描述需要监视哪些文件描述符和对应的事件类型。
  • nfds:表示fds数组中元素的数量。
  • timeout:表示超时时间,单位为毫秒。如果timeout值为负数,则表示永远等待;如果timeout值为0,则表示立即返回。

五、IO多路复用——epoll

在这里插入图片描述

需要包含头文件#include <sys/epoll.h>

1、epoll_create

epoll_create函数是用于创建一个新的 epoll 实例,它的原型如下:
int epoll_create(int size);
其中,size 参数指定了 epoll 实例中允许监听的最大文件描述符数量。该函数返回一个非负整数作为 epoll 句柄,如果出现错误则返回 -1。
注意:在 Linux 2.6.8 以前版本中,epoll_create 函数只接受一个参数,即 epoll 实例大小将被忽略。而在新版本中,则必须传递一个大于0的值作为实例大小参数。

2、epoll_ctl

epoll_ctl()函数是Linux内核提供的用于控制epoll实例的系统调用函数之一,它可以用来添加、修改或删除需要监听的文件描述符以及相应事件。其原型如下:
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
参数说明:

  • epfd:表示要操作的epoll实例标识符。
  • op:表示要进行的操作类型,有以下三种取值:
    EPOLL_CTL_ADD:将指定文件描述符加入到监听队列中;
    EPOLL_CTL_MOD:修改已经加入监听队列中的文件描述符对应事件信息;
    EPOLL_CTL_DEL:将已经加入监听队列中的文件描述符移除。
  • fd:表示需要被操作的文件描述符。
  • event:指向一个结构体变量,用来设置需要监听的事件类型以及相关属性。

在使用epoll_ctl()函数时,需要先创建一个epoll实例,并使用EPOLL_CTL_ADD操作将待监听的文件描述符添加到该实例中。如果后续需要修改所监听事件类型或者属性,则可以使用EPOLL_CTL_MOD操作。当不再需要继续监听某个文件描述符时,则可以使用EPOLL_CTL_DEL操作将其从监控列表中删除。

3、epoll_wait

epoll_wait函数是Linux内核提供的用于异步IO操作的系统调用函数之一,它可以用于等待一个或多个文件描述符上的事件发生,并在事件发生时通知用户进程。该函数与epoll_create和epoll_ctl一起使用来管理非阻塞I/O文件描述符。
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
参数说明:

  • epfd:表示要监控的epoll实例标识符。
  • events:指向存储返回事件的结构体数组。
  • maxevents:表示最大监听事件数。
  • timeout:超时时间,以毫秒为单位,如果timeout为负数则表示永久等待。

当有事件到达时,epoll_wait会将所有就绪的事件存放在传入的events数组中,并返回就绪事件数量。对于每个就绪的文件描述符,需要通过判断events[i].events字段中位设置情况来确定具体是哪种类型的事件(例如可读、可写等)。同时,在处理完就绪事件后,需要将相应文件描述符重新加入到epoll监听队列中。

4、epoll_event

epoll_event是一个结构体,用于描述一个文件描述符上的事件。其定义如下:struct epoll_event { uint32_t events; // 表示监听的事件类型 epoll_data_t data; // 用户数据,可以是指针或者整数值 };
其中events字段表示需要监听的事件类型,取值如下:

  • EPOLLIN:表示该文件描述符上有可读数据。
  • EPOLLOUT:表示该文件描述符上可以写数据。
  • EPOLLRDHUP:表示对端关闭连接或者半关闭连接,即收到FIN包。
  • EPOLLHUP:表示该文件描述符被挂起,可能是对端进程崩溃或者其他错误情况导致的。
  • EPOLLERR:表示出错。

5、边缘触发和水平触发)

另外,epoll还提供了ET(边缘触发)和LT(水平触发)两种工作模式。

  • 水平触发模式
    在水平触发模式下,如果文件描述符上的事件没有被处理完毕,epoll 会持续通知应用程序该文件描述符上仍有事件待处理。在这种情况下,如果应用程序不及时响应并读取数据,则 epoll 会一直通知应用程序该文件描述符上有数据可读取。
  • 边沿触发模式
    在边沿触发模式下,只要文件描述符上出现新的事件(例如数据可读或连接建立),epoll 就会通知应用程序。但是,在通知之后,如果应用程序没有立即响应并读取所有数据,则 epoll 不会再次通知该文件描述符上有新的数据可读。

总体来说,边沿触发模式相比于水平触发模式更为高效,并且可以避免由于重复监听导致 CPU 占用率过高的问题,一般用于数据量很大,需要分批次接收的时候。但是,在使用边沿触发模式时需要注意及时读取所有数据,并确保每个事件都得到了正确处理。
需要注意的是,EPOLLET模式下,并不会丢失数据。即如果数据未全部接收,此时又发送新的数据,接收的时候将先接收上一次的数据。并且,epoll默认是EPOLLLT。
在这里插入图片描述

五、区别对比

1、select和poll

select和poll都是用于多路复用I/O的系统调用函数,可以同时监控多个文件描述符上的事件。它们的主要区别如下:

  • 可以处理的文件描述符数量不同
    select支持最大1024个文件描述符,而poll没有限制。
  • select采用轮询方式,poll采用链表方式
    select将所有待检测的文件描述符放在一个fd_set集合中,每次轮询时需要遍历整个集合;而poll将所有待检测的文件描述符放在一个链表中,每次检查时只需要遍历该链表即可。
  • select支持几乎所有操作系统,poll仅支持部分操作系统
    select是标准POSIX接口,在几乎所有操作系统上都能使用;而poll则不是标准接口,在一些老旧的操作系统上可能无法使用。
  • select对于返回状态码不够清晰明了,而poll更加直观
    select返回后需要使用FD_ISSET宏来判断哪些文件描述符已经就绪;而poll返回后直接通过revents字段来判断哪些文件描述符已经就绪。
  • select效率较低,因为每次都要重新设置fd_set集合;poll效率较高
    由于select内核实现有许多缺陷,所以每次使用前都需要重新设置fd_set集合;而poll没有这个问题,所以效率更高。

总体来说,poll比select更加灵活、可靠,而且效率也更高。但由于select是标准接口,在一些特殊的情况下还是有其用武之地。

2、poll和epoll

poll和epoll都是用于I/O多路复用的系统调用,可以同时监视多个文件描述符是否有数据可读或可写。但是它们有以下区别:

  • 处理方式不同
    poll采用轮询的方式扫描所有的文件描述符,每次扫描时需要遍历整个被监控的文件描述符集合。如果被监控的文件描述符集合很大,那么就会带来较大的开销。
  • 而epoll采用事件通知机制,只有在发生事件时才对该事件进行处理。这样可以大幅减少轮询带来的开销,提高效率。
  • 监听对象数量不同
    poll可以监听的文件描述符数量受限于操作系统中一个进程能打开的最大文件数目。如果要监听更多的文件描述符,则需要增加进程打开文件数目限制,但是这会占用更多系统资源。
  • 而epoll没有监听对象数量上限,因为它采用基于事件驱动模式,在处理完一个事件后,并不删除该事件对应的结构体,所以支持万级别甚至百万级别以上并发连接。
  • 内核与用户空间交互方式不同
    poll每次调用都需要将所有监控的fd集合从用户空间拷贝到内核空间,而epoll只需要一次拷贝,然后在内核中对其进行操作,避免了多次拷贝的开销。

3、select和epoll

select和epoll都是用于I/O多路复用的系统调用,主要用于同时处理多个文件描述符的输入输出事件。但是它们之间存在一些不同点:

  • 操作系统支持程度:select是POSIX标准中定义的函数,可以在大多数操作系统上使用,而epoll只能在Linux操作系统上使用。
  • 处理方式:select采用轮询方式来检查文件描述符的状态变化,每次调用都需要将所有待监控的文件描述符从用户空间拷贝到内核空间,并且每次返回时需要遍历整个集合。而epoll通过回调机制,在文件描述符就绪时直接通知应用程序。
  • 所监视的文件描述符数量:select所能监视的文件描述符数量是有限制的,通常为1024或2048个。而epoll没有此限制,可以监视大量的文件描述符。
  • 内存开销:由于select需要将所有待监控的文件描述符从用户空间拷贝到内核空间,并且每次返回时需要遍历整个集合,因此会产生较大的内存开销。而epoll只需将被触发事件的fd放入一个链表中即可,因此内存开销较小。

总体来说,相比select函数,epoll具有更高效、更灵活、更强大等优势,在高并发场景下性能更佳。

六、代码

子不语

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

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

相关文章

字节面试过了,薪资都谈好了20K*13,结果挂在这里....

一般提到面试&#xff0c;肯定都会想问一下面试结果&#xff0c;我就大概的说一下面试结果&#xff0c;哈哈&#xff0c;其实不太想说&#xff0c;因为挺惨的&#xff0c;并没有像很多大佬一样 ”已拿字节阿里腾讯各大厂offer”&#xff0c;但是毕竟是自己的经历&#xff0c;无…

让效果图渲染做到最佳的几个小诀窍

制作出优秀的效果图需要多方面的技术支持&#xff0c;而渲染是其中非常关键的一步。一份精美的效果图需要经过高质量的渲染才能呈现出最佳的效果。本文将分享一些关于如何让效果图渲染做到最佳的小诀窍&#xff0c;包括专注的小细节、优化场景设置和灯光、纹理、图像应用最终修…

医疗电子红外线人体额温枪方案

在当前新冠疫情背景下&#xff0c;红外线人体额温枪成为疫情防控必备的设备之一。红外线人体额温枪采用红外线技术&#xff0c;无需接触人体&#xff0c;通过测量人体表面温度来判断人体是否发热。其测量快速、准确、不接触等特点&#xff0c;使其广泛应用于机场、车站、医院、…

数据结构之二叉树(Java)

在这里先说明一下&#xff0c;结点和节点其实一样的&#xff0c;无须关注这个。 一、树型结构 1. 概念&#xff1a;树是一种非线性的数据结构&#xff0c;它是由n个有限节点组成一个具有层次关系的集合。 如上图所示&#xff0c;把此种数据结构称作树是因为它看起来像一个倒挂…

校园高校教务选课成绩系统node.js+vue

系统主要实现了以下功能模块&#xff1a; 学籍信息 网上选课 网上评教&#xff0c;即对自己老师评分 任务查询&#xff0c;老师可以给学生发布任务&#xff08;类似于作业&#xff0c;考试&#xff09; 课表查询&#xff0c;希望有个图形化的课表&#xff0c;而不是表格式 教室…

Mysql InnoDB的Buffer Pool

Buffer Pool 在MySQL服务器启动的时候就向操作系统申请了⼀⽚连续的内存&#xff0c;他们给这⽚内存起了个名&#xff0c;叫做Buffer Pool&#xff08;中⽂名 是缓冲池&#xff09;。 默认情况下Buffer Pool只有128M⼤⼩&#xff0c;最⼩值为5M&#xff0c;通过修改配置文件设…

【Linux开发—多进程编程】

【Linux开发—多进程编程】 前言1&#xff0c;两种类型的服务端2&#xff0c;并发服务器的实现方法&#xff1a; 一&#xff0c;认识及应用1&#xff0c;进程认识2&#xff0c;CPU核的个数与进程数3&#xff0c;进程ID4&#xff0c;进程创建5, 调用fork函数后的程序运行流程: 二…

一文了解PWA——Progressive Web App

近年来&#xff0c;出现了一种新的应用开发模式——PWA&#xff08;Progressive Web App&#xff0c;即渐进式Web应用&#xff09;。今天就来为大家分享什么是PWA&#xff0c;以及它与小程序的区别、技术原理。 PWA的基本概念和技术原理 PWA是一种基于Web技术的应用开发模式&a…

基于CC2530设计智慧农业控制系统

一、项目背景 智慧农业是近年来发展迅速的领域,其目的是利用先进的传感技术、物联网技术和云计算技术等,实现自动化、智能化的农业生产管理,并提高农业生产效率和质量。本文基于CC2530设计了一种智慧农业控制系统,采用DHT11模块、BH1750模块和土壤湿度传感器等传感器,通过…

turtle和matplotlib画画:圆形、爱心

马上就要到六一儿童节了&#xff0c;小朋友很喜欢画画&#xff0c;这里就用Pyhton来画一些简单形状&#xff1a; 用turtle画圆形 import turtle# 设置画笔颜色和粗细 turtle.pencolor("black") turtle.pensize(5)# 绘制一个半径为100的圆形 turtle.circle(100)# 隐藏…

jsonp的实现原理

什么是跨域&#xff1a; 跨域是浏览器同源策略而产生的&#xff0c;在不同协议&#xff0c;不同端口&#xff0c;不同域名下&#xff08;以上任意一个不同都算是跨域&#xff09;的客服端和服务端之间是无法互相访问的。 举例&#xff1a; http://www.baidu.com/index.html …

Springboot +spring security,前后端分离时的security处理方案(一)

一.简介 在前后端分离这样的开发模式下&#xff0c;前后端的交互都是通过 JSON 来进行数据传递的&#xff0c;无论登录成功还是失败&#xff0c;都不会有服务端跳转或者客户端跳转之类的操作。 也就是说无论登录成功还是失败&#xff0c;服务端都会返回一段登录成功或失败的 …

与传统序列化比,PB更快更节省空间

文章目录 为何选择PBPB安装WindowsMac未完待续 语法命令行编译Maven插件编译UDP通信的例子 3大序列化方法对比 为何选择PB 在网络传输和存储数据的时候比传统的 JSON 效果更好 PB安装 GitHub Windows 下载 配置环境变量 验证 Mac未完待续 后续补充Mac安装方式 语法 使用过…

Word、Excel、PPT题库——“办公自动化”

小雅兰期末加油冲冲冲&#xff01;&#xff01;&#xff01; 1.【单选题】下列文件扩展名,不属于Word模板文件的是&#xff08; A &#xff09;。 A. .DOCX B. .DOTM C. .DOTX D. .DOT 本题的考查点是word基本知识的了解。 .DOCX&#xff1a;word文档。 .DOTM&#xff1a;启…

目标检测:RPN — Faster R-CNN 的主干

动动发财的小手&#xff0c;点个赞吧&#xff01; 在使用 R-CNN 的目标检测中&#xff0c;RPN 是真正的主干&#xff0c;并且到目前为止已被证明非常有效。它的目的是提出在特定图像中可识别的多个对象。 这种方法是由 Shaoqing Ren、Kaiming He、Ross Girshick 和 Jian Sun 在…

Pandas 28种常用方法使用总结

Pandas库专为数据分析而设计&#xff0c;它是使Python成为强大而高效的数据分析环境的重要因素。它提供了多种数据结构和方法来处理和分析数据。下面是一些Pandas常用方法的使用总结。 1. 创建数据框 使用read_csv()或read_excel()方法读取数据文件&#xff0c;也可以使用Dat…

饶派杯XCTF车联网安全挑战赛Reverse GotYourKey

文章目录 一.程序逻辑分析二.线程2的operate方法解析三.找出真flag 一.程序逻辑分析 onCreate方法中判断SDK版本是否>27 然后创建两个线程 第一个线程是接受输入的字符串并发送出去 第二个线程用于接受数据 线程1,就是将字符串转为字节数组发送出去 线程2,作为服务端接受…

knife4j、swagger、springdoc 返回接口分组排序问题

一、直击问题 解决前后顺序对比 解决方法&#xff1a; 在配置文件中添加排序规则方法sortTagsAlphabetically&#xff1a; package com.example.demo.config;import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.enums.Security…

OpenMMlab的整体概述和作用

是什么&#xff1f; 开源算法体系&#xff08;非框架、有开源代码&#xff09; 用pytorch实现 优势 开箱即用&#xff0c;复现了很多顶会论文中的算法。每个cv任务对应算法库&#xff0c;其中顺序即为学习路线。便于对比实验。 使用统一的框架、超参数&#xff0c;做对比实…

测试接口遇到APP加密?先来了解一下算法思路~

背景 服务端与客户端进行http通讯时&#xff0c;为了防止被爬虫&#xff0c;数据安全性等&#xff0c;引入APP通信加密&#xff0c;简单来说&#xff0c;就是引入签名sign&#xff0c;APP的所有请求都会经过加密签名校验流程。常见的加密方案有AES加密&#xff0c;RSA加密&…