3

Luasocket选择函数应该告诉何时可以无阻塞地读取套接字。它显然也可以用来判断服务器套接字何时准备好接受新连接,但是文档给出了以下警告:

另一个重要的注意事项:在调用 accept 之前在接收参数中使用服务器套接字调用 select 并不能保证 accept 会立即返回。使用 settimeout 方法,否则 accept 可能会永远阻塞。

即使 select 告诉它可以安全阅读,在什么情况下可以接受块?出于测试目的,有没有办法强制发生此问题?

4

2 回答 2

2

我不知道他们从哪里得到这个想法。在 20 多年的网络编程中从未见过它。

如果您有多个 select() 线程,当然会发生这种情况,但是如果这是预期的,我希望文档会这样说。

于 2013-04-15T02:19:10.097 回答
1

accept这是从已故 W.Richard Stevens 的“Unix Network Programming”第 461-463 页的第三版的第 16.6 节(非阻塞)中总结出来的。UNP 可能仍然是最好的关于编写网络代码的教科书。

尽管您可能认为accept不能阻塞之后select表明侦听套接字已准备好,但 Stevens 描述了一些网络堆栈实现中的竞争条件,它可能导致accept无限期阻塞。(脚注将描述归因于“A.Gierth”)。这个问题是通过一个 echo 客户端来描述的,它:

  1. 连接到服务器;

  2. 在连接的套接字上设置SO_LINGER套接字选项;

  3. 立即关闭套接字。由于SO_LINGER已设置选项,关闭套接字会导致发送RST(重置)。

现在,让我们假设服务器正在运行,但在负载很重的机器上。修改后的 echo 客户端运行。TCP 连接会导致select调用返回并指示有可用的连接。(请记住,连接实际上已被内核接受并放入接受队列;accept不需要执行此操作。)

但是,在调用执行之前,服务器代码被进程切换中断,accept同时,客户端设法完成步骤(2)和(3)。然后内核收到客户端的reset,现在连接不再有效。因此,它可能会将其从接受队列中删除。

因此,当服务器代码accept开始建立连接时,没有要接受的连接,并且accept调用阻塞直到下一个连接,如果有的话。

上述行为实际上可能不会发生。即使接受队列中有另一个可用连接(您还必须记住要处理),POSIX 也希望accept调用失败。根据史蒂文斯的说法:ECONNABORTED

在第 5.11 节中,我们注意到当客户端在服务器调用 `accept` 之前中止连接时,Berkeley 派生的实现不会将中止的连接返回给服务器,而其他实现应该返回 `ECONNABORTED` 但通常会返回 `EPROTO` .

史蒂文斯的源代码可在出版商网站上找到;修改后的客户端是nonblock/tcpcli03.c,而对服务器的修改只是在调用之前休眠五秒钟accept。因此,您可以在任何可用的系统上进行尝试。

我不相信 FreeBSD 或 Linux 不再表现出伯克利派生的行为,尽管我很确定我记得它发生在 FreeBSD 上(那可能是十多年前的事了,而且我不再手边有一个 FreeBSD 盒子对其进行测试。)OpenBSD 似乎已在 1999 年进行了修补以解决该问题(请参阅2.4 的补丁);可能其他伯克利衍生物后来也做了类似的改变。我不知道 MacOSX(尽管它可能与 FreeBSD 相同)或 Windows。很可能没有现代系统表现出这种行为,尽管当 Stevens 编写 UNP 时它肯定是可以观察到的。

无论如何,史蒂文斯的建议很简单,小心一点总不会有坏处。他的建议是:

  1. 使用时始终将侦听套接字设置为非阻塞select

  2. 如果acceptEWOULDBLOCKECONNABORTEDEPROTO或失败EINTR,则忽略错误并返回select循环。

于 2013-04-18T03:20:57.573 回答