TCP选项SO_LINGER(零) – 需要时
我想我理解这个选项的forms意义。 在我现在处理的一些遗留代码中,使用了该选项。 客户抱怨RST作为对FIN侧面的反应。
我不知道我可以安全地将其删除,因为我不明白它何时应该使用。
你能举个例子说明什么时候需要这个选项吗?
将SO_LINGER
超时设置为零的典型原因是为了避免处于TIME_WAIT
状态的大量连接占用服务器上的所有可用资源。
当TCP连接完全closures时,发起closures的结束(“主动closures”)结束,连接在TIME_WAIT
中几分钟。 所以如果你的协议是服务器启动连接closures,并涉及大量的短期连接,那么它可能容易受到这个问题的影响。
这不是一个好主意,但是 – TIME_WAIT
存在的原因是为了确保来自旧连接的杂散数据包不会干扰新的连接。 如果可能的话,将协议重新devise为客户端启动连接的协议是一个更好的主意。
对于我的build议,请阅读最后一节: “何时使用SO_LINGER超时0” 。
在我们来讲一讲关于:
- 正常的TCP终止
-
TIME_WAIT
-
FIN
,ACK
和RST
正常的TCP终止
正常的TCP终止序列看起来像这样(简化):
我们有两个同伴:A和B.
- 来电
close()
- A将
FIN
发送给B - A进入
FIN_WAIT_1
状态
- A将
- B收到
FIN
- B向A发送
ACK
- B进入
CLOSE_WAIT
状态
- B向A发送
- A收到
ACK
- A进入
FIN_WAIT_2
状态
- A进入
- B调用
close()
- B将
FIN
发送给A - B进入
LAST_ACK
状态
- B将
- A收到
FIN
- A向B发送
ACK
- A进入
TIME_WAIT
状态
- A向B发送
- B收到
ACK
- B进入
CLOSED
状态 – 即从套接字表中删除
- B进入
时间的等待
因此,启动终止的对等方(即先调用close()
将以TIME_WAIT
状态结束。
要理解为什么TIME_WAIT
状态是我们的朋友,请阅读Stevens等人(第43页)的第三版“UNIXnetworking编程”第2.7节。
但是,在服务器的TIME_WAIT
状态下,有很多套接字可能会出现问题,因为它最终可能会阻止接受新的连接。
要解决这个问题,我已经看到许多build议在调用close()
之前设置SO_LINGER套接字选项的超时值为0。 但是,这是一个错误的解决scheme,因为它会导致TCP连接终止并出现错误。
相反,请devise您的应用程序协议,以便始终从客户端发起连接终止。 如果客户端总是知道何时读取了所有剩余的数据,则可以启动终止序列。 作为一个例子,浏览器从Content-Length
HTTP头中知道它已经读取了所有的数据,并且可以开始closures。 (我知道在HTTP 1.1中它将保持打开一段时间,以便可能的重用,然后closures它。)
如果服务器需要closures连接,请devise应用程序协议,以便服务器要求客户端调用close()
。
何时使用SO_LINGER超时0
再次,根据“UNIXnetworking编程”第三版第202-203页,在调用close()
之前将SO_LINGER
设置为timeout 0将导致正常的终止序列不被启动。
相反,peer设置这个选项并且调用close()
会发送一个RST
(连接重置)来表示错误情况,这就是它在另一端会被感知到的。 您通常会看到诸如“由对等方重置连接”之类的错误。
因此,在正常情况下,在服务器应用程序中调用close()
(从现在开始称为“ 终止closures” close()
之前,将SO_LINGER
设置为超时0是一个非常糟糕的主意。
然而,无论如何,某些情况下仍然需要这样做:
- 如果您的服务器应用程序的客户端出现exception(超时,返回无效数据等),则终止closures有意义,以避免陷入
CLOSE_WAIT
或终止于TIME_WAIT
状态。 - 如果您必须重新启动当前有数千个客户端连接的服务器应用程序,则可以考虑设置此套接字选项以避免
TIME_WAIT
数千个服务器套接字close()
从服务器端调用close()
时),因为这可能会阻止服务器获得可用的端口为重新启动后的新客户端连接。 - 在上述书中的第202页中,它特别指出:“在某些情况下,使用此function可以发送一个exceptionclosures,例如一个RS-232terminal服务器,它可能永远挂在
CLOSE_WAIT
试图将数据发送到卡住terminal端口,但是如果有RST
丢弃未决数据,就会正确地重置卡住的端口。
我会推荐这篇长篇文章,我相信这个文章会给你一个很好的答案。
当连接处于打开状态但超时时间为零时,TCP堆栈不会在closures连接之前等待未决数据的发送。 数据可能由于这个而丢失,但通过设置这种方式,你接受这一点,并要求连接重置一下,而不是优雅地closures。 这导致RST被发送而不是通常的FIN。
感谢EJP的评论,详情请看这里 。