【Linux Socket C++】为什么IO复用需要用到非阻塞IO?EAGAIN的简单介绍与应用

article/2025/10/6 13:43:26

目录

为什么IO复用需要非阻塞的IO

EAGAIN的介绍

EAGAIN的应用


为什么IO复用需要非阻塞的IO

我们可以先看一下官方的回答:

在Linux命令行输入:man 2 select

找到[BUGS],如下:

官方给予的回答是这样的:

Under  Linux, select() may report a socket file descriptor as "ready for reading", while never? theless a subsequent read blocks.  This could for example happen when data has arrived but upon examination  has  wrong checksum and is discarded.  There may be other circumstances in which a file descriptor is spuriously reported as ready.  Thus it may be safer  to  use  O_NONBLOCK  on sockets that should not block.

也就是说:当某个socket接收缓冲区有新数据分节到达,然后select报告这个socket描述符可读,但随后,协议栈检查到这个新分节检验和错误,然后丢弃这个分节,这时候调用read则无数据可读,如果socket没有设置nonblocking,此read将阻塞当前线程

简单来说就是:当数据到达socket缓冲区的时候,select会报告这个socket可读,但是随后因为一些原因,比如校验和错误,内核丢弃了这个数据,这个时候,如果采用了阻塞的IO,唤醒的程序去读取一个已经被丢弃的数据,肯定读不到,所以就会一直卡在那里,也就是阻塞,例如accept和recv函数就会阻塞。

所以这也就是IO复用需要非阻塞IO的原因之一。

原因二:达到缓冲区的数据有可能被别人抢走,比如多个进程accept同一个socket时引发的惊群现象,只有一个客户端连接到来,但是所有的监听程序都会被唤醒,最终只能有一个进程可以accept到这个请求,如果采用阻塞的IO,其他进程的accept就会产生阻塞。

原因三:ET(Edge-triggered)边缘模式下,必须要使用到非阻塞的IO,因为程序中需要循环读和写,直到EAGAIN的出现,如果使用阻塞的IO就容易被阻塞住。

EAGAIN的介绍

这里提一下有关EAGAIN的知识

EAGAIN是一个错误代码,在Linux环境下开发经常会碰到很多错误(设置errno),其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中)。

man accept找到EAGAIN的解释如下:

原文是这样说的:

The  socket  is  marked  nonblocking  and  no  connections  are  present to be accepted.
POSIX.1-2001 allows either error to be returned for this  case,  and  does  not  require these  constants to have the same value, so a portable application should check for both
possibilities. 

翻译过来的意思大概就是:如果socket的状态为非阻塞,但是accept函数没有找到可用的连接,就会返回EAGAIN错误。

我们时常会用一个while(true)死循环去接收缓冲区中客户端socket的连接,如果这个时候我们设置socket状态为非阻塞,那么accept如果在某个时间段没有接收到客户端的连接,因为是非阻塞的IO,accept函数会立即返回,并将errno设置为EAGAIN.

EAGAIN的应用

EAGAIN应用示例一:accept

我们时常会在死循环中设置if(errno==EAGAIN) break;

因为我们死循环只是为了接收缓冲区中的连接,一旦accept在缓冲区找不到可用的连接了,那么accept会将errno设置为EAGAIN,这个时候我们只需要判断errno==EAGAIN就说明了,accept已经将缓冲区中的连接读取完,所以可以直接break了。

EAGAIN应用示例二:recv、send

前提:非阻塞的IO、EPOLLET边缘触发模式

recv:在EPOLLIN|EPOLLET监视可读、边缘触发模式下,recv函数会时刻关注缓冲区中是否有数据可读,如果缓冲区中有数据未处理,EPOLLET模式下的epoll只会汇报一次socket有可读事件,当有新的数据加入缓冲区时,就会再次汇报可读。

根据上面的说明,假设现在缓冲区中有1000字节待recv读取,ET模式下会触发一次可读事件,为了避免我们我们读取不完缓冲区的数据(也就是说假设我们的recv目前一次只能读取200字节,所以一次肯定读取不玩),这个时候我们又会使用到死循环while(true),让recv一直读取缓冲区中的内容。我们知道缓冲区如果没有新的内容,是不会再触发可读事件的,所以当recv读取完缓冲区的内容之后,因为是非阻塞的IO模式,recv不会等待立即返回,并且会将errno设置为EAGAIN,此时的EAGAIN表示没有数据可以读取。

因此,我们可以像accept函数那样,在死循环里判断errno,也就是if(errno==EAGAIN) break;

send:在EPOLLOUT|EPOLLET监视可写、边缘触发模式下,send函数会时刻关注发送缓冲区是否已满,如果发送缓冲区未满,EPOLLET模式下就会触发一次可写事件,只有当缓冲区从满变为"有空"的时候,才会再次触发一次可写事件(假设缓冲区大小为1000,缓冲区中有300字节内容,客户端读取比服务端发送要快,这个时候缓冲区内容减少,但不会触发可写事件)。

根据上面的说明,假设缓冲区1000字节就满了,现如今缓冲区中有300字节的内容,那么ET模式会触发一次可写事件,在这个可写时间的处理代码中,你可以向客户端发送你想要发送的内容。当写入缓冲区满了的时候,因为是非阻塞IO,send会立即返回,并将errno设置为EAGAIN,此时的EAGAIN表示缓冲区内容已满,无法继续写入,所以我们也没必要让send阻塞在那里。

因此,我们可以像accept函数那样,当然循环的出口还是我们说的if(errno==EAGAIN) break;

EAGAIN这个错误代码在非阻塞IO的程序中会经常出现,上面只是一些经常使用到EAGAIN的场景;有时我们可以不把它看作是一个错误,而看作是一个工具,一个来寻找循环出口的工具。

以上便是文章的全部内容,如果讲解有误,还请指正,谢谢大家观看!


http://chatgpt.dhexx.cn/article/j1sp7HgZ.shtml

相关文章

avcodec_receive_frame始终返回EAGAIN

今天我们研究一个问题: avcodec_receive_frame()始终返回EAGAIN 根本的解决方案还需要深入debug,但是这个函数很太复杂,需要些时间和耐心; 目前在不考虑编解码性能的情况下,能work around的方法只有一个,那…

APK加固(梆梆助手)

前言:朋友在使用梆梆时出现Apk加固后安装失败的现象,所以自己写篇小白文 1.进入梆梆官网(注册) 2.下载梆梆助手(点击加固工具) 本人电脑是Windows系统(下载) 3.安装后&#xff0c…

使用360进行apk加固并进行2次签名整体流程

因新版360加固助手需要付费才能进行自动签名,故只能自己手动来签名了~ 1.使用Android studio进行首次签名并打包apk 首先选择build下该选项 选择apk 如果没有key,则点击新建 需要输入key存储的位置,key store密码,key别名&#…

Android-APK加固-简单版

Android-APK加固-简单版 Proguard的使用与配置介绍开启proguard常用配置 加固大体思路 源码(浅析)思路 撸码解密工具类-AES(解密时用)工具类-Zip(压缩、解压)工具类-Utils(反射操作)…

Android Apk加固后手动签名

手动签名 : 不用任何第三方可视化工具签名 ,使用命令做签名。手动签名原因:以前加固签名都是使用第三方工具操作,最近发现工具都开始收费了,免费的羊毛没得薅了,收费价格极高 5000/年/App, (加固…

手写apk加固

手写apk加固 加壳解压原apk并加密重命名dex文件对壳文件操作打包压缩成apk文件签名 脱壳运行解压原apk, 解密原dex文件加载原dex文件 demo下载 apk加固的目的其实就是对app的核心代码做防护工作,避免被其他人反编译; 废话不多说了,直接开始! …

android apk 加固后重新签名

针对于加固平台在加固的过程中不能配置签名文件,加固后的apk需要进行重新签名才能安装,并发布到应用市场。 第一步,用AS对项目进行打包,生成签名的apk文件。 第二步,使用加固平台,对apk包进行加固&#xff…

Android Apk加固原理解析

前言 为什么要加固 对APP进行加固,可以有效防止移动应用被破解、盗版、二次打包、注入、反编译等,保障程序的安全性、稳定性。 常见的加固方案有很多,本文主要介绍如果通过对dex文件进行加密来达到apk加固的目的; APK加固整体…

apk加固后再签名

目录 前言v1签名v1v2签名 前言 apk更新之前需要做安全检测,检测之前一版会做加固处理,加固后还需要重新进行签名。本文介绍一下v1签名和v1v2签名两种方式。 有文章说需要把apk原来的签名文件,即META-INF文件夹删除,实测不删好像也…

Android apk 加固混淆的作用之解决apk报毒

现在市面上对apk的安全合规管控越来越严格了,也就要求了apk在上架之前一定要做合规检测和加固处理。对apk就是加固的好处,可以提高apk的安全性,提高apk被逆向分析破解的门槛,同时通过加固保护可以提高过安全合规的检测。由于APP加…

简书 android 加固,Android apk加固(加壳)整理

一、Dex加壳由来 最近在学习apk加密,在网上看了一篇《Android中的Apk的加固(加壳)原理解析和实现》,我发现原文把整个apk都写入到dex文件中,如果apk小还好,当原APK大于200M,客户端解壳很费劲,打开后应用就卡…

019 Android加固之APK加固的原理和实现

文章目录 前言加载Activity遇到的问题APK的启动过程替换ClassLoader流程获取ActivityThread类对象获取AppBindData类对象mBoundApplication获取LoadedApk类对象info获取info对象中的ClassLoader 设计傀儡dex文件手工加固APK代码实现APK加固实现步骤 总结 前言 动态加载dex之后…

【Android 安全】Android 应用 APK 加固总结 ( 加固原理 | 应用加固完整的实现方案 | 源码资源 )

文章目录 一、 APK 加固原理1、 Android 应用反编译2、 ProGuard 混淆3、 多 dex 加载原理4、 代理 Application 开发5、Java 工具开发6、Application 替换 二、 应用加固完整的实现方案1、 代理 Application( 1 ) ProxyApplication( 2 ) OpenSSL 解码 Kotlin 类( 3 ) 反射工具…

android资源加固,Android apk加固实现原理

apk加固是每一个app发布之前必须要做的事情;如果一个apk没有加固那么别人就很容易被别人反编译,看到这其中的原码,虽然现在有代码混淆、把业务写到native层,但是这都是治标不治本。反编译的技术在更新,那么保护Apk的技术就不能停止…

Android中Apk加固代码实现

前言:上一篇博客已经把Apk加固的思路详细的介绍过了,也开始创建了一个空的demo进行,然后在项目中添加一个代理module(解密,和系统源码交互功能)和tools工具加密Java library 的module ,这里开始…

Android APK加固原理

一、前言 Android作为开源框架,开放之余,所要面临的就是安全问题,世间之事,有正就有邪,有攻就有守,作为开发者虽然不需要进入专业安全领域,但还是需要掌握基本的安全常识和原理。 二、加壳 加…

APK加固原理详解

一、前言 之前使用的360加固,挺好用的,从2021年底的时候限制每天每个账号仅上传2次apk(免费的,不知道VIP的是不是这样)。通过这个事情,感觉技术还是掌握在自己手里稳妥点,不用受制于人&#xf…

Android中的Apk的加固(加壳)原理解析和实现

本文转载自:Android中的Apk的加固(加壳)原理解析和实现 - roccheung - 博客园 一、前言 今天又到周末了,憋了好久又要出博客了,今天来介绍一下Android中的如何对Apk进行加固的原理。现阶段。我们知道Android中的反编译工作越来越让人操作熟…

浅谈安卓apk加固原理和实现

转载本文需注明出处:微信公众号EAWorld,违者必究。 引言: 在安卓开发中,打包发布是开发的最后一个环节,apk是整个项目的源码和资源的结合体;对于懂点反编译原理的人可以轻松编译出apk的源码资源&#xff0c…

安卓逆向笔记--apk加固

安卓逆向笔记–apk加固 资料来源: 浅谈安卓apk加固原理和实现 Android中的Apk的加固(加壳)原理解析和实现 前两个太老了所以具体代码借鉴下面的 Android Apk加壳技术实战详解 一、apk常见加固方法 (1)代码层级加密–代码混淆 代码混淆是一种常见的加密方式。本质是把工程中原…