对于我的建议,请阅读最后一节:“何时使用超时为 0 的 SO_LINGER”.
在我们开始之前,先听一个关于以下内容的小讲座:
- 正常 TCP 终止
TIME_WAIT
-
FIN
, ACK
and RST
正常 TCP 终止
正常的 TCP 终止序列如下所示(简化):
我们有两个对等点:A 和 B
- A calls
close()
- A sends
FIN
to B
- A 进入
FIN_WAIT_1
state
- B 收到
FIN
- B sends
ACK
to A
- B 进入
CLOSE_WAIT
state
- A 收到
ACK
- B calls
close()
- B sends
FIN
to A
- B 进入
LAST_ACK
state
- A 收到
FIN
- A sends
ACK
to B
- A 进入
TIME_WAIT
state
- B 收到
ACK
时间的等待
因此发起终止的对等方 – 即调用close()
首先 – 最终将在TIME_WAIT
state.
要了解为什么TIME_WAIT
状态是我们的朋友,请阅读 Stevens 等人的《UNIX 网络编程》第三版(第 43 页)中的 2.7 节。
但是,如果有大量套接字,这可能会出现问题TIME_WAIT
服务器上的状态,因为它最终可能会阻止接受新连接。
为了解决这个问题,我看到很多人建议在调用之前将 SO_LINGER 套接字选项设置为超时 0close()
。然而,这是一个糟糕的解决方案,因为它会导致 TCP 连接因错误而终止。
相反,请设计您的应用程序协议,以便始终从客户端发起连接终止。如果客户端始终知道何时读取了所有剩余数据,则它可以启动终止序列。举个例子,浏览器从Content-Length
当 HTTP 标头已读取所有数据并可以发起关闭时。 (我知道在 HTTP 1.1 中它会保持打开状态一段时间以便可能重用,然后关闭它。)
如果服务器需要关闭连接,请设计应用程序协议,以便服务器要求客户端调用close()
.
何时使用超时为 0 的 SO_LINGER
再次,根据《UNIX网络编程》第三版第202-203页,设置SO_LINGER
调用前超时为 0close()
将导致正常的终止序列not待发起。
相反,对等方设置此选项并调用close()
将发送一个RST
(连接重置)表示错误情况,这就是另一端的感知方式。您通常会看到诸如“连接被对等方重置”之类的错误。
因此,在正常情况下,设置是一个非常糟糕的主意SO_LINGER
调用前超时为 0close()
– 从现在开始叫失败的关闭– 在服务器应用程序中。
但是,无论如何,某些情况都需要这样做:
- 如果服务器应用程序的客户端行为不当(超时、返回无效数据等),则中止关闭可以避免陷入困境
CLOSE_WAIT
或最终在TIME_WAIT
state.
- 如果您必须重新启动当前具有数千个客户端连接的服务器应用程序,您可以考虑设置此套接字选项以避免数千个服务器套接字
TIME_WAIT
(当打电话时close()
从服务器端),因为这可能会阻止服务器在重新启动后获取新客户端连接的可用端口。
- 在上述书中的第 202 页,它特别指出:“在某些情况下,需要使用此功能来发送中止关闭。一个例子是 RS-232 终端服务器,它可能会永远挂在
CLOSE_WAIT
尝试将数据传送到卡住的终端端口,但如果收到错误消息,则会正确重置卡住的端口RST
丢弃待处理的数据。”
我会推荐this http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.html我相信这篇长文章很好地回答了您的问题。