梦想还在,生活当继续!
一、前言
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 unavailable,errno 代码为11(EAGAIN),这是什么意思?
这表明你在非阻塞模式下调用了阻塞操作,在该操作没有完成就返回这个错误,这个错误不会破坏 socket 的同步,不用管它,下次循环接着 recv 就可以。
两种答案不同,但是感觉都很有道理。这让我迷惑了,于是决定自己研究研究。记录下来,分享的同时,也方便自己以后回顾。
二、BlockingIOError
首先,我想知道 python 中的 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, EWOULDBLOCK和 EINPROGRESS。”
linux 下,BlockingIOError: [Errno 11] 即为 EAGAIN 错误。
windows 上,EAGAIN 的名字叫做 EWOULDBLOCK。对应的报错信息为:
“BlockingIOError: [WinError 10035] 无法立即完成一个非阻止性套接字操作”。
官网的这个说明,依然没让我明白为什么会出现“资源不可用”。但它大概描述了如何触发 EAGAIN,于是我决定复现它,继续研究。
三、EAGAIN 复现
据我了解,可以触发 EAGAIN 错误的操作有:accept()、recv()、send()。
connect() 方法也会阻塞,但返回的是 EINPROGRESS