什么时候需要TCP选项SO_LINGER(0)?


95

我认为我了解该选项的正式含义。在我现在处理的一些旧代码中,使用了该选项。客户抱怨RST是从侧面对FIN的响应。

我不确定我可以安全地删除它,因为我不知道何时应该使用它。

您能否举例说明何时需要使用该选项?


1
您应该将其删除。不应在生产代码中使用它。我见过的唯一一次使用它的原因是基准测试无效。
2014年

Answers:


82

SO_LINGER超时设置为零的典型原因是避免大量连接处于该TIME_WAIT状态,从而占用服务器上的所有可用资源。

完全关闭TCP连接后,发起关闭的一端(“活动关闭”)将在TIME_WAIT几分钟内结束连接。因此,如果您的协议是服务器用来发起关闭连接的协议,并且涉及大量的短期连接,那么它可能很容易受到此问题的影响。

不过,这不是一个好主意- TIME_WAIT存在是有原因的(以确保来自旧连接的杂散数据包不会干扰新连接)。如果可能的话,最好将协议重新设计为客户端发起连接关闭的协议。


3
我完全同意。我看到了一个监视应用程序,该应用程序启动了许多应用程序(每X秒创建几千个短寿命连接),并且有扩大规模的探针(更多的一千个连接)。我不知道为什么,但是申请没有回应。有人建议SO_LINGER = true,TIME_WAIT = 0以快速释放OS资源,经过简短调查,我们确实尝试了此解决方案,并获得了很好的结果。TIME_WAIT不再是此应用程序的问题。
bartosz.r 2012年

24
我不同意。位于TCP之上的应用程序级别协议的设计方式应使客户端始终启动连接关闭。这样一来,TIME_WAIT遗嘱就不会伤害到客户。请记住,它在“ UNIX网络编程”第三版(Stevens等人)第203页中说过:“ TIME_WAIT状态是您的朋友,可以为我们提供帮助。我们不应该避免这种状态,而应该避免这种状态(第2.7节) 。”
mgd 2012年

8
如果客户端希望每30秒打开4000个连接怎么办(此监视应用程序是客户端!因为它启动了连接)?是的,我们可以重新应用程序,在基础架构中添加一些本地代理,更改模型以进行推送。但是,如果我们已经有了这样的应用程序并且它不断增长,那么我们可以通过调整tlinger使它运行。您更改了一个参数,突然有了可以使用的应用程序,而没有投入预算来实施新的体系结构。
bartosz.r 2012年

3
@ bartosz.r:我只是说,将SO_LINGER与超时0一起使用确实是最后的选择。再次,在“ UNIX网络编程”第三版(Stevens等)的第203页中,它还说您有遭受数据损坏的风险。考虑阅读RFC 1337,在这里您可以了解TIME_WAIT为什么是您的朋友。
mgd 2012年

7
@caf不,经典的解决方案将是一个连接池,在每个重型TCP API(例如HTTP 1.1)中都可以看到。
洛恩侯爵,

188

对于我的建议,请阅读最后一节:“何时在超时0时使用SO_LINGER”

在我们进行有关以下内容的简短演讲之前:

  • 正常TCP终止
  • TIME_WAIT
  • FINACKRST

正常TCP终止

正常的TCP终止顺序如下所示(简化):

我们有两个同行:A和B

  1. 一个电话 close()
    • 发送 FIN给B
    • A进入FIN_WAIT_1状态
  2. B收到 FIN
    • B发送 ACK给A
    • B进入CLOSE_WAIT状态
  3. A收到 ACK
    • A进入FIN_WAIT_2状态
  4. B电话 close()
    • B发送 FIN给A
    • B进入LAST_ACK状态
  5. A收到 FIN
    • 发送 ACK给B
    • A进入TIME_WAIT状态
  6. B收到 ACK
    • B进入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()

何时在超时0时使用SO_LINGER

同样,根据“ UNIX网络编程”第三版第202-203页,SO_LINGER在调用之前设置超时0 close()将导致正常终止序列无法启动。

相反,设置此选项并调用的对等方close()将发送一个RST(连接重置)消息,该消息指示错误情况,这就是在另一端被感知的方式。通常,您会看到诸如“由对等方重置连接”之类的错误。

因此,在正常情况下,在SO_LINGER调用之前将超时设置为0 是一个非常糟糕的主意close()–从现在开始,调用终止关闭在服务器应用程序中。

但是,在某些情况下仍然需要这样做:

  • 如果服务器应用程序的客户端行为不当(超时,返回无效数据等),则异常中止关闭是有意义的,以避免卡在状态中CLOSE_WAIT或最终陷入该TIME_WAIT状态。
  • 如果必须重新启动当前具有数千个客户端连接的服务器应用程序,则可以考虑设置此套接字选项,以避免TIME_WAITclose()从服务器端调用时)插入数千个服务器套接字,因为这可能会阻止服务器获取新客户端连接的可用端口重新启动后。
  • 在上述书籍的第202页上,它特别指出:“在某些情况下,需要使用此功能发送中止关闭消息。一个示例是RS-232终端服务器,在CLOSE_WAIT试图将数据传送到卡住的终端时可能永远挂起。端口,但如果它RST可以丢弃待处理的数据,则可以正确重置卡住的端口。”

我会推荐这篇长文章,我相信它可以很好地回答您的问题。


6
TIME_WAIT是朋友,只有当它不启动的原因的问题:stackoverflow.com/questions/1803566/...
Pacerier

2
那么如果您正在编写Web服务器呢?您如何“告诉客户发起关闭”?
Shaun Neal

2
@ShaunNeal您显然不知道。但是编写良好的客户端/浏览器将启动关闭。如果客户端的行为不正常,则幸运的是我们有TIME_WAIT暗杀,以确保我们不会用完套接字描述符和临时端口。
mgd

16

当延迟开启但超时为零时,TCP堆栈在关闭连接之前不会等待发送待处理的数据。因此,数据可能会丢失,但是通过以这种方式进行设置,您就可以接受并要求立即重置连接,而不是优雅地关闭连接。这将导致发送RST,而不是通常的FIN。

感谢EJP的评论,有关详细信息,请参见此处


1
我明白了 我要问的是当我们想使用硬重置时的“现实”示例。
dimba 2010年

5
每当您想中止连接时;所以如果你的协议验证失败,你在你有一个客户端胡说八道,一下子你会中止与RST等连接
莱恩霍尔盖特

5
您将零延迟超时与延迟关闭混淆了。延迟关闭意味着close()不会阻塞。延迟时间为正值表示继续执行close()直到超时为止。超时为零会导致RST,这就是问题所在。
2014年

2
是的,你是对的。我将调整答案以更正我的术语。
Len Holgate 2014年

6

是否可以安全地删除代码中的延迟取决于应用程序的类型:是“客户端”(打开TCP连接并首先主动关闭它)还是“服务器”(侦听TCP打开和关闭)?在另一方发起关闭后将其关闭)?

如果您的应用程序具有“客户端”的风格(先关闭),并且您启动和关闭与不同服务器的大量连接(例如,当您的应用程序是监视应用程序,监督大量不同服务器的可达性时)有一个问题,您的所有客户端连接都停留在TIME_WAIT状态。然后,我建议将超时时间缩短为小于默认值的值,以便仍然正常关闭,但更早地释放客户端连接资源。我不会将超时设置为0,因为0不会通过FIN正常关闭,但会通过RST中止。

如果您的应用程序具有“客户端”的风格,并且必须从同一服务器中获取大量小文件,则不应为每个文件启动新的TCP连接,而在TIME_WAIT中最终导致大量客户端连接,但是保持连接打开并通过同一连接获取所有数据。流浪者选项可以并且应该删除。

如果您的应用程序是一个“服务器”(作为对端关闭的响应,第二个关闭),则在close()上,您的连接将正常关闭,并且由于不进入TIME_WAIT状态,因此资源被释放。不应使用流连忘返。但是,如果您的服务器应用程序具有检测长时间闲置(定义为“ long”)的非活动打开连接的监控过程,则可以从您的侧面关闭此非活动连接-将其视为一种错误处理-异常中止关闭。这是通过将linger超时设置为0来完成的。close()然后将向客户端发送RST,告诉他您很生气:-)


1

在服务器中,您可能希望发送RST而不是FIN断开行为不正常的客户端时的发送。这会跳过服务器中FIN-WAITTIME-WAIT套接字状态,从而防止耗尽服务器资源,从而保护其免受此类拒绝服务攻击。


0

我喜欢Maxim的观点,即DOS攻击会耗尽服务器资源。在没有真正恶意对手的情况下也会发生这种情况。

某些服务器必须处理“意外的DOS攻击”,这种攻击是在客户端应用程序存在连接泄漏的bug时发生的,它们会继续为发送给服务器的每个新命令创建一个新的连接。然后,如果遇到GC压力,则可能最终关闭它们的连接,或者最终连接会超时。

另一种情况是“所有客户端具有相同的TCP地址”的情况。然后,只能通过端口号来区分客户端连接(如果它们连接到单个服务器)。而且,如果客户端由于某种原因而开始快速循环打开/关闭连接,它们将耗尽(客户端地址+端口,服务器IP +端口)元组空间。

因此,我认为服务器最好在看到大量TIME_WAIT状态的套接字时切换到Linger-Zero策略-尽管它不能解决客户端的问题,但可以减少影响。


0

服务器上的侦听套接字可以使用时间0徘徊,可以立即绑定回套接字,并可以重置所有尚未完成连接的客户端。TIME_WAIT仅在您具有多路径网络且可能会以未按顺序排列的数据包结束或以其他方式处理奇数网络数据包的排序/到达时间时才有意义。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.