python3「非阻塞socket」报错 “BlockingIOError: [Errno 11]“ 复现以及分析解决

本文详细分析了Python3中使用非阻塞socket通信时遇到的BlockingIOError: [Errno 11]错误,通过复现错误、原因分析和解决方案探讨,解释了该错误与资源不可用的关系,并提供了相应的错误处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

梦想还在,生活当继续!

一、前言

linux 下,用 python 的非阻塞 socket 通信时,遇到了 BlockingIOError: [Errno 11] Resource temporarily unavailable 错误。

翻译报错信息 Resource temporarily unavailable 为:“资源暂时不可用”。

在我的代码里,使用了“epoll + 非阻塞 socket” 的模式。因此猜测,在有 socket 还未创建完成的情况下,使用它发送消息导致报错,错误的理解为这个 socket 资源暂时不可用。-.-

后来上网查找相关资料,发现并非如此。根据网上资料,我得到两种不同的答案。

答案一:

首先,是我把套接字设置为异步的。然后,在使用 write 发送数据时采取的方式是循环发送大量的数据;由于是异步的,write\send 将要发送的数据提交到发送缓冲区后是立即返回的,并不需要对端确认数据已接收。在这种情况下是很有可能出现发送缓冲区被填满,导致 write\send 无法再向缓冲区提交要发送的数据。因此就产生了 Resource temporarily unavailable 的错误。EAGAIN 的意思也很明显,就是要你再次尝试。

答案二:

Linux 进行非阻塞的 socket 接收数据时经常出现 Resource temporarily unavailableerrno 代码为11(EAGAIN),这是什么意思?
这表明你在非阻塞模式下调用了阻塞操作,在该操作没有完成就返回这个错误,这个错误不会破坏 socket 的同步,不用管它,下次循环接着 recv 就可以。

两种答案不同,但是感觉都很有道理。这让我迷惑了,于是决定自己研究研究。记录下来,分享的同时,也方便自己以后回顾。

二、BlockingIOError

首先,我想知道 python 中的 BlockingIOError 具体指什么。
python 官方文档中,找到了对 BlockingIOError 异常的说明,如下:

exception BlockingIOError:

Raised when an operation would block on an object (e.g. socket) set for non-blocking operation. Corresponds to errno EAGAIN, EALREADY, EWOULDBLOCK and EINPROGRESS.

我将它翻译为:“当在设置为非阻塞操作的对象(例如:套接字)上,执行阻塞操作时触发。对应的错误类型有:EAGAIN, EALREADY, EWOULDBLOCKEINPROGRESS。”

linux 下,BlockingIOError: [Errno 11] 即为 EAGAIN 错误。

windows 上,EAGAIN 的名字叫做 EWOULDBLOCK。对应的报错信息为:
BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作”。

<think>好的,我现在要解决用户提出的问题:“rev可以正常工作且返回值大于零,send无法发送且返回值为零”。这个问题涉及到两个函数或方法的返回值情况,需要分析可能的原因。首先,我需要明确用户提到的rev和send具体是什么功能,可能是在网络编程中接收和发送数据的函数,比如在socket编程中的recv和send方法。 用户提到rev(可能是指recv)可以正常工作且返回值大于零,这说明接收数据没有问题。而send无法发送,返回值为零,这需要进一步分析。根据常见的网络编程知识,send函数返回0可能有几种情况。比如,在非阻塞模式下,send返回0可能表示发送缓冲区已满,无法立即发送数据;或者在TCP连接中,对方已经关闭连接,导致本端发送数据时返回0。另外,如果发送的数据长度参数为0,也可能返回0,但这种情况通常是用户代码的问题。 接下来,我需要分步骤分析可能的原因。首先检查send函数的参数是否正确,比如套接字描述符、缓冲区地址、数据长度以及标志位是否正确。然后确认网络连接是否正常,是否已经被对方关闭。可以使用其他方法如recv的返回值来判断连接状态,如果recv返回0,说明对方已经关闭连接。此外,检查是否处于非阻塞模式,如果是,可能需要处理EWOULDBLOCK或EAGAIN错误,这时应该等待可写事件再重试发送。 另外,还需要考虑发送缓冲区的状态。如果缓冲区已满,send可能会阻塞或返回错误,具体取决于套接字的阻塞模式。如果是阻塞模式,send会一直等待直到有空间;如果是非阻塞模式,则立即返回错误。用户提到的send返回0,可能是在非阻塞模式下,缓冲区已满,此时应检查errno以确定具体错误。 还需要确认发送的数据长度是否确实大于零,避免尝试发送空数据的情况。此外,检查是否有信号中断导致send被中断,这时可能需要重新尝试发送。 总结可能的解决步骤:检查连接状态,确认对方没有关闭连接;检查套接字的阻塞模式,如果是非阻塞模式,处理缓冲区满的情况;验证发送数据的有效性和长度;检查错误代码以获取更多信息;最后,使用调试工具或日志进一步定位问题。 可能还需要用户提供更多上下文信息,比如代码片段、使用的编程语言、操作系统环境等,以便更准确地诊断问题。例如,在Python中,socket.send()返回0的情况可能与C语言中的不同,需要具体分析。此外,不同操作系统对socket行为的实现可能有细微差别,这也是需要考虑的因素。 最后,建议用户逐步排查,从验证连接状态开始,检查发送参数,处理非阻塞情况,并查看系统错误代码,以确定send返回0的具体原因,从而采取相应的解决措施。</think>针对“rev可以正常工作且返回值大于零,send无法发送且返回值为零”的问题,可能涉及网络编程中的`recv`和`send`函数行为。以下是逐步分析解决方案: --- ### **1. 明确函数行为** - **`recv`返回值>0**:表示成功接收到数据,且数据长度为返回值。 - **`send`返回0**:需结合上下文分析,常见原因如下: - **非阻塞模式下**:发送缓冲区已满,无法立即发送。 - **连接已关闭**:对方关闭了连接,本地继续调用`send`可能返回0。 - **发送数据长度为0**:若传给`send`的第三个参数(数据长度)为0,函数直接返回0。 --- ### **2. 检查连接状态** - **若`recv`返回0**:表示对方已关闭连接,此时`send`可能失败。 - **解决方法**:关闭当前套接字,重新建立连接。 - **若`recv`仍正常工作**:需检查本地是否主动关闭了连接(如调用`close`或`shutdown`)。 --- ### **3. 验证发送参数** 确认调用`send`的参数是否正确: ```c // 示例:send函数原型 ssize_t send(int sockfd, const void *buf, size_t len, int flags); ``` - **`sockfd`**:套接字描述符是否有效? - **`len`**:是否传递了正数长度?若`len=0`,函数直接返回0。 - **`buf`**:发送缓冲区地址是否有效? --- ### **4. 处理非阻塞模式** - **若套接字设为非阻塞**: - 当发送缓冲区已满时,`send`可能返回-1,并设置`errno`为`EAGAIN`或`EWOULDBLOCK`。 - **特殊场景**:若`send`返回0,可能是缓冲区恰好满且用户要求发送0字节(需检查代码逻辑)。 - **解决方法**: 1. 检查`errno`确认具体错误: ```c if (bytes_sent == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) { // 等待可写事件后重试 } } ``` 2. 使用`select`或`epoll`监听套接字的可写事件。 --- ### **5. 检查协议逻辑** - **TCP连接状态**:若对方已关闭连接,本地继续发送数据会触发`SIGPIPE`信号(默认终止进程),或`send`返回-1且`errno=EPIPE`。 - **解决方法**:忽略`SIGPIPE`信号,并处理`EPIPE`错误: ```c signal(SIGPIPE, SIG_IGN); // 忽略信号 if (send(...) == -1 && errno == EPIPE) { // 重新连接或终止 } ``` --- ### **6. 调试建议** - **打印错误信息**:通过`strerror(errno)`获取具体错误原因。 - **数据有效性**:确保发送的缓冲区内容有效(如非空指针)。 - **最小化复现**:编写简化代码验证问题,例如: ```c char data[] = "test"; ssize_t ret = send(sockfd, data, strlen(data), 0); printf("send returned %zd, errno=%d\n", ret, errno); ``` --- ### **总结步骤** 1. 确认连接是否正常(对方未关闭)。 2. 检查`send`参数(长度>0,缓冲区有效)。 3. 处理非阻塞模式下的缓冲区满问题。 4. 捕获并分析`errno`明确错误类型。 5. 必要时重启连接或优化发送逻辑(如分片发送)。 若问题仍存在,请提供代码片段及操作系统环境,以便进一步定位。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员的一天

~呀哈,感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值