epoll与socket缓冲区的恩恩怨怨

文章目录

  • 前言
  • 一、什么是socket缓冲区
  • 二、阻塞与非阻塞内核缓冲区
    • 1、如果发送缓冲区满了会怎么样
      • 阻塞
      • 非阻塞
    • 2、如果接受缓冲区为空会怎么样
      • 阻塞
      • 非阻塞
  • 三、epoll与缓冲区的恩恩怨怨
    • 水平触发
    • 边缘触发
      • 非阻塞
      • 阻塞
    • 结论


前言

本文深挖网络编程中的缓冲区,从什么是缓冲区出发,然后分析epoll 两个模式使用阻塞与非阻塞缓冲区的区别,

一、什么是socket缓冲区

TCP三次握手成功,TCP连接成功建立后,操作系统内核会为每个连接创建配套的基础设施,包括 发送缓冲区,其大小可通过套接字选项改变。程序调用write函数时,实际所做的事是把数据从应用程序中拷贝到操作系统内核中。既然是写给操作系统,那操作系统就需要提供一个地方给用户写。同理,接收消息也是一样。这个地方就是 socket 缓冲区。

也就是说一个socket ,会带有两个缓冲区,一个用于发送,一个用于接收。因为这是个先进先出的结构,有时候也叫它们发送、接收队列。在这里插入图片描述

二、阻塞与非阻塞内核缓冲区

使用TCP建立连接之后,一般会使用 send 发送数据。执行 send 之后,数据只是拷贝到了socket 缓冲区。至 什么时候会发数据,发多少数据,全听操作系统安排。根据实际情况(比如拥塞窗口等)判断是否要发数据。如果不发送数据,那么此时直接返回

阻塞IO:当你去读一个阻塞的文件描述符时,如果在该文件描述符上没有数据可读,那么它会一直阻塞(通俗一点就是一直卡在调用函数那里),直到有数据可读。当你去写一个阻塞的文件描述符时,如果在该文件描述符上没有空间(通常是缓冲区)可写,那么它会一直阻塞,直到有空间可写。以上的读和写我们统一指在某个文件描述符进行的操作,不单单指真正的读数据,写数据,还包括接收连接accept(),发起连接connect()等操作…

非阻塞IO:当你去读写一个非阻塞的文件描述符时,不管可不可以读写,它都会立即返回,返回成功说明读写操作完成了,返回失败会设置相应errno状态码,根据这个errno可以进一步执行其他处理。它不会像阻塞IO那样,卡在那里不动!!

1、如果发送缓冲区满了会怎么样

发送缓冲区有足够的空间,可以用于拷贝待发送数据。如果发送缓冲区空间不足,或者满了,执行发送,会怎么样?这里分两种情况。

阻塞

如果此时 socket 是阻塞的,那么程序会在那干等、死等,直到释放出新的缓存空间,就继续把数据拷进去,然后返回。在这里插入图片描述

非阻塞

如果此时 socket 是非阻塞的,程序就会立刻返回一个 EAGAIN 错误信息,意思是 Try again , 现在缓冲区满了,你也别等了,待会再试一次。在这里插入图片描述

2、如果接受缓冲区为空会怎么样

阻塞

如果此时 socket 是阻塞的,那么程序会在那干等,直到接收缓冲区有数据,就会把数据从接收缓冲区拷贝到用户缓冲区,然后返回。在这里插入图片描述

非阻塞

如果此时 socket 是非阻塞的,程序就会立刻返回一个 EAGAIN 错误信息。在这里插入图片描述

三、epoll与缓冲区的恩恩怨怨

我们都知道epoll()模型即支持水平触发,也支持边缘触发,默认是水平触发。
Level_triggered(水平触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写,当然如果你一直不去读写,它会一直通知你!!!如果系统中有大量你不需要读写的就绪文件描述符,而它们每次都会返回,这样会大大降低处理程序检索自己关心的就绪文件描述符的效率!!!
Edge_triggered(边缘触发):当被监控的文件描述符上有可读写事件发生时,epoll_wait()会通知处理程序去读写。如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用epoll_wait()时,它不会通知你,也就是它只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知你!!!这种模式比水平触发效率高,系统不会充斥大量你不关心的就绪文件描述符!!

首先明确read函数以及recv函数分别在阻塞和非阻塞状态的时候他们对缓冲区有没有数据的反应

 ret = recv(sockfd, buffer, MAX_BUFFER_SIZE, 0);

在非阻塞状态下:
1、如果没有数据可读,并且没有发生错误,recv() 返回值为 -1,同时 errno 被设置为 EAGAIN 或 EWOULDBLOCK。这表示当前没有可读数据,并且需要稍后再次尝试读取。
2、如果连接已被关闭(对方已关闭连接),recv() 返回值为 0,表示读取到的数据长度为 0,这意味着连接已经结束。
3、如果发生其他错误,recv() 返回值为 -1,并且 errno 被设置为相应的错误代码,例如 ECONNRESET 表示连接被重置,ETIMEDOUT 表示连接超时等。
在阻塞的状态下
如果是阻塞模式,read() 函数或 recv() 函数在没有数据可读时会一直阻塞,直到有数据可读或者出现错误。在此期间,程序会一直等待,并且不会执行其他操作。如果连接被关闭或者发生错误,read() 或 recv() 函数会立即返回相应的错误代码

水平触发

在水平触发的情况下,设置非阻塞与阻塞他们的效果都是一样的。不过为了防止特殊情况,还是建议设置非阻塞。
个人认为,因为在水平触发的情况下,缓冲区有数据就会通知处理程序去读写。如果这次没有把数据一次性全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait()时,它还会通知你在上没读写完的文件描述符上继续读写。那么水平触发就保证每次缓冲区都是有数据的,就不会出现阻塞connfd遇到缓冲区为空的情况了。

  /* LT处理流程 */
void epoll_lt(int sockfd){char buffer[MAX_BUFFER_SIZE];int ret;memset(buffer, 0, MAX_BUFFER_SIZE);printf("开始recv()...\n");ret = recv(sockfd, buffer, MAX_BUFFER_SIZE, 0);printf("ret = %d\n", ret);if (ret > 0)printf("收到消息:%s, 共%d个字节\n", buffer, ret);else{if (ret == 0)printf("客户端主动关闭!!!\n");close(sockfd);}printf("LT处理结束!!!\n");}

实验
设置应用缓冲区大小为5,编译运行后,用一个客户端连接,并发送1-9这几个数:
在这里插入图片描述
再看服务器的反映,可以看到水平触发触发了2次。因为我们代码里面设置的缓冲区是5字节,处理代码一次接收不完,水平触发一直触发,直到数据全部读取完毕:
在这里插入图片描述

边缘触发

由于边缘触发只会通知你一次,直到该文件描述符上出现第二次可读写事件才会通知。所以要用循环来处理。

/* 带循环的ET处理流程 */
void epoll_et_loop(int sockfd){char buffer[MAX_BUFFER_SIZE];int ret;printf("带循环的ET读取数据开始...\n");while (1){memset(buffer, 0, MAX_BUFFER_SIZE);ret = recv(sockfd, buffer, MAX_BUFFER_SIZE, 0);if (ret == -1){// 非阻塞的时候 没有东西读了 就会返回-1if (errno == EAGAIN || errno == EWOULDBLOCK){printf("循环读完所有数据!!!\n");break;}close(sockfd);break;}else if (ret == 0){printf("客户端主动关闭请求!!!\n");close(sockfd);break;}elseprintf("收到消息:%s, 共%d个字节\n", buffer, ret);}printf("带循环的ET处理结束!!!\n");
}

非阻塞

在非阻塞的情况下,epoll_wait()一次提醒进入循环之后,会不断地读取一定长度的数据,知道套接字上没有新的数据到达时,recv() 函数会立即返回 -1,并且退出。
用一个客户端连接,并发送1-9。再观测服务器的反映,可以看到数据全部读取完毕,处理函数也退出了,因为非阻塞IO如果没有数据可读时,会立即返回,并设置error,这里我们根据EAGAIN和EWOULDBLOCK来判断数据全部读取完毕了,可以退出循环了:在这里插入图片描述

阻塞

用一个客户端连接,发送1-9。看看服务器,可以看到数据全部读取完毕:
在这里插入图片描述
程序没有输出"带循环的ET处理结束",是因为程序一直卡在了recv()函数上,因为是阻塞IO,如果没数据可读,它会一直等在那里,直到有数据可读。如果这个时候,用另一个客户端去连接,服务器不能受理这个新的客户端!!

结论

所以ET模式下必须设为非阻塞模式。循环读取的时候,如果缓冲区没有数据或者低于水位线,recv/read就会阻塞等待读事件就绪,这会影响到epoll模型中其他文件描述符的操作。

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

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

相关文章

数据结构 | 树

树 树是n(n>0)个结点的有限集。当n 0时,称为空树。在任意一棵非空树中应满足: 有且仅有一个特定的称为根的结点。当n>1时,其余节点可分为m(m>0)个互不相交的有限集T1,T2,…,Tm&#…

【Android】线程下载资源保证资源到位采用了 OkHttp的三方网络下载 文件缓存策略

背景 使用 SVGA的三方的url播放方式会比较慢,至少延迟3s以上才会出现svga效果,所以改变策略:将线上的svga全部下载到本地进行播放,那么就得将采用网络缓存的方式实现效果。 实现 那么就得实现以下几点: 初次下载缓…

专栏更新情况:华为流程、产品经理、战略管理、IPD

目录 前言 01 华为流程体系入门课 CSDN学院 02 产品经理进阶课 CSDN学院 03 BLM 战略方法论进阶课 04 IPD 进阶 100 例专栏 作者简介 前言 已上线四大课程专栏更新情况: 01 华为流程体系入门课(视频图文); 02 硬件产品经…

UE4/5数字人MetaHuman通过已有动画进行修改

目录 通过已有动画修改动画 开始制作 创建一个关卡序列 将动画序列烘焙到控制绑定 打开我们自己创建的动画序列 之后便是烘焙出来 通过已有动画修改动画 首先架设我们已经有相关的MetaHuman的动画,但是这个动画因为是外部导入进来的,所以可能会出…

处理conda安装工具的动态库问题——解决记录 libssl.1.0.0 系统中所有openssl位置全览 whereis openssl

处理conda安装工具的动态库问题——解决记录 处理conda安装工具的动态库问题——解决记录 - 简书 解决libssl.so.1.0.0: cannot open shared object file: No such file or directory问题 - 简书 openssl 默认版本问题(Anaconda相关)_anaconda openssl-…

web:[极客大挑战 2019]Upload

题目 页面显示为一个上传&#xff0c;猜测上传一句话木马文件 先查看源代码看一下有没有有用的信息&#xff0c;说明要先上传图片&#xff0c;先尝试上传含有一句话木马的图片 构造payload <?php eval($_POST[123]);?> 上传后页面显示为&#xff0c;不能包含<&…

架构案例2022(四十二)

促销管理系统 某电子商务公司拟升级其会员与促销管理系统&#xff0c;向用户提供个性化服务&#xff0c;提高用户的粘性。在项目立项之初&#xff0c;公司领导层一致认为本次升级的主要目标是提升会员管理方式的灵活性&#xff0c;由于当前用户规模不大&#xff0c;业务也相对…

【笔记】离线Ubuntu20.04+mysql 5.7.36 + xtrabackup定时增量备份脚本

一、环境 ● Ubuntu版本查看 lsb_release -a● mysql 版本查看 mysql --version我的是ubuntu 20.04&#xff0c;mysql是5.7.36&#xff0c;所以要用 install_percona-xtrabackup-24 二、原理 备份 通过ubuntu自带的定时器运行增量备份脚本备份文件可以存储在映射后的其他…

云原生Kubernetes:Pod控制器

目录 一、理论 1.Pod控制器 2.Deployment 控制器 3.SatefulSet 控制器 4.DaemonSet 控制器 5.Job 控制器 6.CronJob 控制器 二、实验 1.Deployment 控制器 2.SatefulSet 控制器 3.DaemonSet 控制器 4.Job 控制器 5.CronJob 控制器 三、问题 1. showmount -e 报错…

华为OD机试 - 判断字符串子序列(Java 2023 B卷 100分)

目录 专栏导读一、题目描述二、输入描述三、输出描述1、输入2、输出3、说明 四、Java算法源码五、效果展示1、输入2、输出 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&am…

京东大型API网关实践之路

概述 1、背景 京东作为电商平台&#xff0c;近几年用户、业务持续增长&#xff0c;访问量持续上升&#xff0c;随着这些业务的发展&#xff0c;API网关应运而生。 API网关&#xff0c;就是为了解放客户端与服务端而存在的。对于客户端&#xff0c;使开放给客户端的接口标准统…

2023-9-28 JZ54 二叉搜索树的第k个结点

题目链接&#xff1a;二叉搜索树的第k个结点 import java.util.*;/** public class TreeNode {* int val 0;* TreeNode left null;* TreeNode right null;* public TreeNode(int val) {* this.val val;* }* }*/public class Solution {/*** 代码中的类名、方…

TVP专家谈腾讯云 Cloud Studio:开启云端开发新篇章

导语 | 近日&#xff0c;由腾讯云 TVP 团队倾力打造的 TVP 吐槽大会第六期「腾讯云 Cloud Studio」专场圆满落幕&#xff0c;6 位资深的 TVP 专家深度体验腾讯云 Cloud Studio 产品&#xff0c;提出了直击痛点的意见与建议&#xff0c;同时也充分肯定了腾讯云 Cloud Studio 的实…

文件审计及文件完整性监控

什么是文件审核 对文件服务器中发生的所有事件的检查称为文件审核。这包括监视文件访问&#xff0c;其中包含谁访问了什么文件、何时以及从何处访问的详细信息;对访问最多和修改的文件的分析;成功和失败的文件访问尝试;等等。文件服务器审核过程的主要目标是跟踪在配置的服务器…

Vue3最佳实践 第六章 Pinia,Vuex与axios,VueUse 1(Pinia)

Pinia状态管理 在 Vue3 中项目中组件之间传递数据时&#xff0c;可以使用 Props 和 Emit&#xff0c;还可以使用 Provide/Inject 来代替 Props 和 Emit。Props 和 Emit 只能在具有父子关系的组件之间传递数据&#xff0c;所以层级越深&#xff0c;过程就越复杂。为了解决此类问…

蓝桥杯 题库 简单 每日十题 day11

01 质数 质数 题目描述 给定一个正整数N&#xff0c;请你输出N以内&#xff08;不包含N&#xff09;的质数以及质数的个数。 输入描述 输入一行&#xff0c;包含一个正整数N。1≤N≤10^3 输出描述 共两行。 第1行包含若干个素数&#xff0c;每两个素数之间用一个空格隔开&…

【IDEA】使用idea调试时查看对象集合的值

1、在实体类上添加toString方法 2、在要查看集合的地方右键View as→toString 3、View Text复制对象集合的值 4、复制map集合的值同理

基于SSM的图书商城系统的设计与实现

基于SSM的图书商城系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringSpringMVCMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 图书列表 图书详情 个人中心 管理员界面 摘要 本文旨在探讨和展示一种基于Spring、…

记一次实战案例

1、目标&#xff1a;inurl:news.php?id URL&#xff1a;https://www.lghk.com/news.php?id5 网站标题&#xff1a;趋时珠宝首饰有限公司 手工基础判断&#xff1a; And用法 and 11: 这个条件始终是为真的, 也就是说, 存在SQL注入的话, 这个and 11的返回结果必定是和正常页…

体验亚马逊的 CodeWhisperer 感觉

CodeWhisperer 是亚马逊推出的辅助编程工具&#xff0c;在程序员写代码时&#xff0c;它能根据其内容生成多种代码建议。 CodeWhisperer 目前已支持近10几种语言&#xff0c;我是用 java 语言&#xff0c;用的开发工具是 idea&#xff0c;说一下我用的情况。 亚马逊云科技开发…