TCP选项SO_LINGER(零) - 何时需要

时间:2010-09-21 04:23:15

标签: sockets networking tcp setsockopt so-linger

我想我理解该选项的正式含义。在我正在处理的一些遗留代码中,使用了该选项。客户抱怨RST作为对其附近连接的FIN的回应。

我不确定我是否可以安全地将其删除,因为我不明白何时应该使用它。

请举例说明何时需要该选项?

8 个答案:

答案 0 :(得分:162)

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

在我们开始讨论之前:

  • 正常TCP终止
  • TIME_WAIT
  • FINACKRST

正常TCP终止

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

我们有两个同行:A和B

  1. 来电close()
    • A将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
    • A将ACK发送给B
    • A进入TIME_WAIT
  6. B收到ACK
    • B进入CLOSED状态 - 即从套接字表中删除
  7. TIME_WAIT

    因此,启动终止的对等方 - 即首先调用close() - 将最终处于TIME_WAIT状态。

    要了解TIME_WAIT州为什么是我们的朋友,请阅读Stevens等人的“UNIX网络编程”第三版第2.7节(第43页)。

    但是,服务器上的TIME_WAIT状态的许多套接字可能会出现问题,因为它最终可能会阻止接受新连接。

    要解决此问题,我已经看到许多建议在调用close()之前将SO_LINGER套接字选项设置为超时0。但是,这是一个糟糕的解决方案,因为它会导致TCP连接因错误而终止。

    相反,请设计您的应用程序协议,以便始终从客户端启动连接终止。如果客户端总是知道它何时读取了所有剩余数据,则它可以启动终止序列。例如,当浏览器读取所有数据并且可以启动关闭时,浏览器会从Content-Length HTTP标头知道。 (我知道在HTTP 1.1中它会保持打开一段时间以便重用,然后关闭它。)

    如果服务器需要关闭连接,请设计应用程序协议,以便服务器要求客户端呼叫close()

    何时使用超时0

    的SO_LINGER

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

    相反,设置此选项并调用close()的对等体将发送RST(连接重置),指示错误情况,这是在另一端感知它的方式。您通常会看到“通过对等方重置连接”等错误。

    因此,在正常情况下,在调用SO_LINGER之前将close()设置为超时0是非常糟糕的 - 从现在开始称为 abortive close - 在服务器应用程序。

    但是,某些情况保证这样做:

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

    我会推荐this长篇文章,我认为这篇文章可以很好地回答你的问题。

答案 1 :(得分:69)

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

当干净地关闭TCP连接时,发起关闭的结束(“主动关闭”)最终会在TIME_WAIT中连接几分钟。因此,如果您的协议是服务器启动连接关闭并且涉及大量短期连接的协议,那么它可能容易受到此问题的影响。

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

答案 2 :(得分:15)

当“间隔”打开但超时为零时,TCP堆栈不会等待在关闭连接之前发送待处理数据。由于这种情况,数据可能会丢失,但是通过这种方式设置逗留,您接受此并要求立即重置连接而不是优雅地关闭。这会导致发送RST而不是通常的FIN。

感谢EJP的评论,请参阅here了解详情。

答案 3 :(得分:4)

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

如果您的应用程序具有“客户端”的风格(先关闭)并且您启动&关闭与不同服务器的大量连接(例如,当您的应用程序是一个监控应用程序,监控大量不同服务器的可访问性)时,您的应用程序会遇到所有客户端连接都处于TIME_WAIT状态的问题。然后,我建议将超时缩短到比默认值更小的值,以便仍然正常关闭但更早地释放客户端连接资源。我不会将超时设置为0,因为0没有使用FIN正常关闭但是使用RST无效。

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

如果您的应用程序是“服务器”(作为对等关闭的反应排在第二位),则在关闭()时,您的连接将正常关闭,并且在您未进入TIME_WAIT状态时释放资源。不应该使用灵儿。但是,如果您的服务器应用程序有一个监控过程,检测到长时间闲置的非活动开放连接(“长”将被定义),您可以从您这边关闭此非活动连接 - 将其视为错误处理类型 - 并且中断关闭。这是通过将linger timeout设置为0来完成的。然后close()会向客户端发送一个RST,告诉他你生气了: - )

答案 4 :(得分:1)

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

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

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

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

答案 5 :(得分:0)

在服务器中,断开行为不正常的客户端时,您可能希望发送RST而不是FIN。这样会跳过服务器中的FIN-WAIT和后跟TIME-WAIT的套接字状态,从而防止耗尽服务器资源,从而避免受到这种拒绝服务攻击。

答案 6 :(得分:0)

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

答案 7 :(得分:0)

我刚刚在 websockets RFC (RFC 6455) 中看到,它明确指出 服务器 应该首先在 TCP 套接字上调用 close()(!)

我很敬畏,因为我将@mgd 在此线程中的答案/帖子视为事实上的,而 RFC 显然与此相反。但是,也许在这种情况下,将延迟时间设置为 0 是可以接受的。

<块引用>

底层的 TCP 连接,在大多数正常情况下,应该关闭 首先由服务器,以便它保持 TIME_WAIT 状态而不是 客户

我很想听听对此的任何想法/见解。