在Luasocket中,即使在select告诉您可以安全读取的情况下,accept调用块在什么情况下仍可以阻止? [英] In Luasocket, under which conditions can an accept call block even after select tells it is safe to read?

查看:414
本文介绍了在Luasocket中,即使在select告诉您可以安全读取的情况下,accept调用块在什么情况下仍可以阻止?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

Luasocket select 函数应该告诉何时套接字可以被读取而不会阻塞.显然,它也可以用来告诉服务器套接字何时准备好接受新连接,但是文档给出了以下警告:

The Luasocket select function is supposed to tell when a socket can be read without blocking. It apparently can also be used to tell when a server socket is ready to accept a new connection however the documentation gives the following warning:

另一个重要说明:在接受接受调用之前,在具有接收方参数的服务器套接字上调用select并不能保证接受会立即返回.使用settimeout方法或accept可能会永远阻止.

Another important note: calling select with a server socket in the receive parameter before a call to accept does not guarantee accept will return immediately. Use the settimeout method or accept might block forever.

在什么情况下即使选择告诉您可以安全阅读,接受也可以阻止?为了测试目的,是否有办法强迫此问题发生?

Under what circumstances can accept block even when select told it was safe to read? Is there a way to force this problem to occur, for testing purposes?

推荐答案

摘要摘自已故W.Richard Stevens的"Unix Network Programming"(Unix网络编程)第三版的16.6节(非阻塞accept). 463. UNP可能仍然是编写网络代码方面最好的可用教科书.

This is summarized from Section 16.6 (Nonblocking accept) of the third edition of the late W.Richard Stevens' "Unix Network Programming", page 461-463. UNP is probably still the best available textbook on writing networking code.

尽管您可能会认为accept在指示了侦听套接字已准备就绪后无法阻塞,但史蒂文斯描述了某些网络堆栈实现中的竞争条件,该竞争条件可能导致accept无限期阻塞. (脚注将描述归因于"A.Gierth").通过回显客户端来描述该问题:

Although you might think that accept cannot block after select indicates that a listening socket is ready, Stevens describes a race condition in some network stack implementations which can cause accept to block indefinitely. (A footnote attributes the description to "A.Gierth"). The problem is described by means of an echo client which:

  1. 连接到服务器;

  1. Connects to the server;

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

Sets the SO_LINGER socket option on the connected socket;

立即关闭套接字.因为已经设置了SO_LINGER选项,所以关闭套接字会导致发送RST(重置).

Immediately closes the socket. Because the SO_LINGER option has been set, closing the socket causes an RST (reset) to be sent.

现在,让我们假设服务器正在运行,但是负载很大.修改后的回显客户端将运行. TCP连接使select调用返回,并指示存在可用的连接. (请记住,该连接实际上已被内核接受,并被放入接受队列; accept不需要执行,就可以实现.)

Now, let's suppose the server is running but on a heavily-loaded machine. The modified echo client is run. The TCP connection causes the select call to return with an indication that there is a connection available. (Remember that the connection was actually accepted by the kernel and put into the accept queue; accept does not need to be executed for this to happen.)

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

However, the server code is interrupted by a process switch before the accept call is executed, and in the meanwhile, the client manages to finish steps (2) and (3). Then the kernel receives the reset from the client, and now the connection is no longer valid. It might, therefore, remove it from the accept queue.

因此,当服务器代码遍历到accept的连接时,没有任何连接可以接受,并且accept调用将阻塞直到下一个连接(如果存在).

So by the time the server code gets around to accepting the connection, there is no connection to accept, and the accept call blocks until the next connection, if there is one.

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

The behaviour described above might not actually happen. POSIX wants the accept call to fail with ECONNABORTED even if there is another available connection in the accept queue (which you also have to remember to deal with). According to Stevens:

在5.11节中,我们注意到当客户端在服务器调用`accept`之前中止连接时,伯克利派生的实现不会将中止的连接返回到服务器,而其他实现应该返回"ECONNABORTED",但通常会返回`改为使用EPROTO`.
In Section 5.11, we noted that when the client aborts the connection before the server calls `accept`, Berkeley-derived implementations do not return the aborted connection to the server, while other implementations should return `ECONNABORTED` but often return `EPROTO` instead.

Stevens的源代码可在发布商的网站上此处获得;修改后的客户端为nonblock/tcpcli03.c,对服务器的修改仅包括在调用accept之前休眠五秒钟.因此,您可以在任何可用的系统上进行尝试.

Stevens' source code is available here, on the publisher's site; the modified client is nonblock/tcpcli03.c, and the modification to the server simply consists of sleeping for five seconds before calling accept. So you can try it on whatever systems you have available.

我不相信FreeBSD或Linux都会再表现伯克利派生的行为,尽管我很确定我记得它发生在FreeBSD上(这可能是十年前的事了,我已经不再拥有FreeBSD盒方便进行测试.)OpenBSD似乎已在1999年进行了修补,以解决此问题(请参见补丁到2.4 );可能其他的伯克利派生派后来也做了类似的修改.我不知道MacOSX(尽管可能与FreeBSD相同)或Windows.尽管当史蒂文斯(Stevens)撰写UNP时确实可以观察到,但是很可能没有现代系统表现出这种行为.

I don't believe that either FreeBSD or Linux exhibit the Berkeley-derived behaviour any more, although I'm pretty sure I remember it happening on FreeBSD (that could have been over a decade ago, and I no longer have a FreeBSD box handy to test it on.) OpenBSD seems to have been patched in 1999 to fix the problem (see patch to 2.4); probably the other Berkeley-derivatives made similar changes later. I have no idea about MacOSX (although it's probably the same as FreeBSD) or Windows. It might well be that no modern system exhibits the behavious, although it was surely observable when Stevens wrote UNP.

无论如何,史蒂文斯的建议很简单,小心一点也不会有伤害.他的建议是:

In any event, Stevens' advice is pretty simple, and it never hurts to be careful. What he suggests is:

  1. 在使用select时,始终将监听套接字设置为非阻塞;

  1. Always set a listening socket to non-blocking when you use select on it;

如果accept失败并显示EWOULDBLOCKECONNABORTEDEPROTOEINTR,请忽略该错误并返回select循环.

If accept fails with EWOULDBLOCK, ECONNABORTED, EPROTO, or EINTR, ignore the error and return to the select loop.

这篇关于在Luasocket中,即使在select告诉您可以安全读取的情况下,accept调用块在什么情况下仍可以阻止?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆