我认为我了解该选项的正式含义。在我现在处理的一些旧代码中,使用了该选项。客户抱怨RST是从侧面对FIN的响应。
我不确定我可以安全地删除它,因为我不知道何时应该使用它。
您能否举例说明何时需要使用该选项?
Answers:
将SO_LINGER
超时设置为零的典型原因是避免大量连接处于该TIME_WAIT
状态,从而占用服务器上的所有可用资源。
完全关闭TCP连接后,发起关闭的一端(“活动关闭”)将在TIME_WAIT
几分钟内结束连接。因此,如果您的协议是服务器用来发起关闭连接的协议,并且涉及大量的短期连接,那么它可能很容易受到此问题的影响。
不过,这不是一个好主意- TIME_WAIT
存在是有原因的(以确保来自旧连接的杂散数据包不会干扰新连接)。如果可能的话,最好将协议重新设计为客户端发起连接关闭的协议。
TIME_WAIT
遗嘱就不会伤害到客户。请记住,它在“ UNIX网络编程”第三版(Stevens等人)第203页中说过:“ TIME_WAIT状态是您的朋友,可以为我们提供帮助。我们不应该避免这种状态,而应该避免这种状态(第2.7节) 。”
对于我的建议,请阅读最后一节:“何时在超时0时使用SO_LINGER”。
在我们进行有关以下内容的简短演讲之前:
TIME_WAIT
FIN
, ACK
和RST
正常的TCP终止顺序如下所示(简化):
我们有两个同行:A和B
close()
FIN
给BFIN_WAIT_1
状态FIN
ACK
给ACLOSE_WAIT
状态ACK
FIN_WAIT_2
状态close()
FIN
给ALAST_ACK
状态FIN
ACK
给BTIME_WAIT
状态ACK
CLOSED
状态–即从套接字表中删除因此,发起终止的对等方(即close()
先呼叫)将最终处于该TIME_WAIT
状态。
要了解为什么TIME_WAIT
状态是我们的朋友,请阅读Stevens等人(第43页)的“ UNIX网络编程”第三版中的2.7节。
但是,这可能是一个问题,其中有许多插座 TIME_WAIT
服务器上因为它最终可能阻止新连接被接受。
要变通解决此问题,我看到很多建议在调用之前将SO_LINGER套接字选项设置为超时0 close()
。但是,这是一个不好的解决方案,因为它会导致TCP连接因错误而终止。
而是设计您的应用程序协议,以便始终从客户端启动连接终止。如果客户端始终知道何时已读取所有剩余数据,则可以启动终止序列。举例来说,浏览器从Content-Length
HTTP标头中何时已读取所有数据并可以启动关闭。(我知道在HTTP 1.1中,它将保持打开状态一段时间,以便可能的重用,然后将其关闭。)
如果服务器需要关闭连接,请设计应用程序协议,以便服务器要求客户端调用 close()
。
同样,根据“ UNIX网络编程”第三版第202-203页,SO_LINGER
在调用之前设置超时0 close()
将导致正常终止序列无法启动。
相反,设置此选项并调用的对等方close()
将发送一个RST
(连接重置)消息,该消息指示错误情况,这就是在另一端被感知的方式。通常,您会看到诸如“由对等方重置连接”之类的错误。
因此,在正常情况下,在SO_LINGER
调用之前将超时设置为0 是一个非常糟糕的主意close()
–从现在开始,调用终止关闭在服务器应用程序中。
但是,在某些情况下仍然需要这样做:
CLOSE_WAIT
或最终陷入该TIME_WAIT
状态。TIME_WAIT
(close()
从服务器端调用时)插入数千个服务器套接字,因为这可能会阻止服务器获取新客户端连接的可用端口重新启动后。CLOSE_WAIT
试图将数据传送到卡住的终端时可能永远挂起。端口,但如果它RST
可以丢弃待处理的数据,则可以正确重置卡住的端口。”我会推荐这篇长文章,我相信它可以很好地回答您的问题。
TIME_WAIT
是朋友,只有当它不启动的原因的问题:stackoverflow.com/questions/1803566/...
当延迟开启但超时为零时,TCP堆栈在关闭连接之前不会等待发送待处理的数据。因此,数据可能会丢失,但是通过以这种方式进行设置,您就可以接受并要求立即重置连接,而不是优雅地关闭连接。这将导致发送RST,而不是通常的FIN。
感谢EJP的评论,有关详细信息,请参见此处。
是否可以安全地删除代码中的延迟取决于应用程序的类型:是“客户端”(打开TCP连接并首先主动关闭它)还是“服务器”(侦听TCP打开和关闭)?在另一方发起关闭后将其关闭)?
如果您的应用程序具有“客户端”的风格(先关闭),并且您启动和关闭与不同服务器的大量连接(例如,当您的应用程序是监视应用程序,监督大量不同服务器的可达性时)有一个问题,您的所有客户端连接都停留在TIME_WAIT状态。然后,我建议将超时时间缩短为小于默认值的值,以便仍然正常关闭,但更早地释放客户端连接资源。我不会将超时设置为0,因为0不会通过FIN正常关闭,但会通过RST中止。
如果您的应用程序具有“客户端”的风格,并且必须从同一服务器中获取大量小文件,则不应为每个文件启动新的TCP连接,而在TIME_WAIT中最终导致大量客户端连接,但是保持连接打开并通过同一连接获取所有数据。流浪者选项可以并且应该删除。
如果您的应用程序是一个“服务器”(作为对端关闭的响应,第二个关闭),则在close()上,您的连接将正常关闭,并且由于不进入TIME_WAIT状态,因此资源被释放。不应使用流连忘返。但是,如果您的服务器应用程序具有检测长时间闲置(定义为“ long”)的非活动打开连接的监控过程,则可以从您的侧面关闭此非活动连接-将其视为一种错误处理-异常中止关闭。这是通过将linger超时设置为0来完成的。close()然后将向客户端发送RST,告诉他您很生气:-)
在服务器中,您可能希望发送RST
而不是FIN
断开行为不正常的客户端时的发送。这会跳过服务器中FIN-WAIT
的TIME-WAIT
套接字状态,从而防止耗尽服务器资源,从而保护其免受此类拒绝服务攻击。
我喜欢Maxim的观点,即DOS攻击会耗尽服务器资源。在没有真正恶意对手的情况下也会发生这种情况。
某些服务器必须处理“意外的DOS攻击”,这种攻击是在客户端应用程序存在连接泄漏的bug时发生的,它们会继续为发送给服务器的每个新命令创建一个新的连接。然后,如果遇到GC压力,则可能最终关闭它们的连接,或者最终连接会超时。
另一种情况是“所有客户端具有相同的TCP地址”的情况。然后,只能通过端口号来区分客户端连接(如果它们连接到单个服务器)。而且,如果客户端由于某种原因而开始快速循环打开/关闭连接,它们将耗尽(客户端地址+端口,服务器IP +端口)元组空间。
因此,我认为服务器最好在看到大量TIME_WAIT状态的套接字时切换到Linger-Zero策略-尽管它不能解决客户端的问题,但可以减少影响。