非阻塞套接字上的select()、recv()和EWOULDBLOCK

10

我想知道以下情况是否真实?!

  1. 在非阻塞TCP套接字上调用select() (读操作) 表明套接字已准备就绪
  2. 随后的recv() 调用返回EWOULDBLOCK,尽管已经调用了select()
8个回答

6

recv()函数将返回EAGAIN而非EWOULDBLOCK,这是可能的。因为你刚才使用了select()进行了查询,所以以下两种情况之一发生:

  • 其他情况(例如其他线程)已经在select()recv()之间处理了输入缓冲区。
  • 套接字上设置了接收超时并且超时时间到期,但未接收到数据。

如果超时,select() 函数会返回 0,所以我不用担心它。 - tonymontana
4
#define EWOULDBLOCK EAGAIN /* 操作将会阻塞 */ -- 在许多操作系统中发现 - blaze
POSIX.1-2001 允许在非阻塞套接字上读取时返回错误(并不要求它们具有相同的值)。 - Marwan Burelle
有趣的是,许多操作系统都定义了相同的错误代码,这意味着您无法将其放入 switch 块以实现可移植性。 - Pork 'n' Bunny

4

4

这是可能的,但只有在多个线程/进程尝试从同一套接字读取时才会出现。


很高兴听到这个消息。我的应用程序是单线程的,所以我没问题。 - tonymontana

3
我注意到一个常见桌面操作系统中存在错误,即O_NONBLOCK TCP套接字,特别是在回环接口上运行时,有时会在select()报告套接字准备好读取后从recv()返回EAGAIN。在我的情况下,在另一端半关闭发送流之后发生这种情况。

更多详情请参阅我的OCaml网络应用程序环境分发中NX库中的t_nx.ml源代码。(链接


1

是的,这是真实的。以下是它发生的一种方式:

TCP协议的未来修改将添加一方有能力“撤销”其发送的信息(前提是另一方的应用层尚未接收到该信息)。此功能在连接时进行协商。另一方向您发送一些数据,您获得一个select命中。在您调用recv之前,另一方使用此新扩展“撤销”数据。您的read因没有可读取的数据而出现“would block”错误。

select函数是一种状态报告函数,不提供未来保证。假设现在对select的命中确保后续操作不会阻塞,就像以任何其他状态报告函数的方式使用一样是无效的。这与使用access尝试确保由于权限不正确而导致后续操作不会失败或使用statfs尝试确保后续写入不会因磁盘已满而失败一样糟糕。


1
尽管我的应用程序是单线程的,但我注意到在RHEL5中描述的行为并不罕见。使用设置为O_NONBLOCK的TCP和UDP套接字(唯一设置的套接字选项),select()报告套接字已准备就绪,但随后的recv()返回EAGAIN。

0

在多线程环境中,两个线程从套接字读取数据是可能的。这是一个多线程应用程序吗?


即使套接字上有超时而不仅仅是 select() 的参数?如果超时发生在 select()recv() 之间怎么办? - dwc
在这种情况下,你不应该看到这种行为。 - mikelong
你说的“套接字超时”是什么意思? - tonymontana

-1
如果在 select() 和 recv() 之間沒有調用任何其他的系統調用,那麼 recv() 將永遠不會返回 EAGAIN 或 EWOULDBLOCK。
我不知道 recv-timeout 的含義,然而 POSIX 標準在這裡並未提到它,所以可以放心調用 recv()。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接