我正在阅读有关使用 SO_LINGER 套接字选项通过将逗留时间设置为零来故意“暗杀”时间等待状态的信息。这本书的作者接着说我们永远不应该这样做,而且一般来说我们永远不应该干预时间等待状态。然后他立即建议使用 SO_REUSEADDR 选项绕过时间等待状态。
我的问题是,有什么区别?在这两种情况下,您都过早地终止了时间等待状态并冒着接收重复段的风险。为什么一个好一个坏?
TIME_WAIT 是绝对正常的。它发生在本地端的 TCP FIN 后跟来自远程位置的 TCP FIN ACK 之后。在 TIME_WAIT 中,您只是在等待任何杂散数据包到达本地地址。但是,如果有一个丢失或杂散的数据包,则 TIME_WAIT 确保 TTL 或“生存时间”在再次使用该地址之前到期。
如果您使用 SO_REUSEADDR 那么您基本上是在说,我会假设没有杂散数据包。现代、可靠的 TCP 网络越来越有可能发生这种情况。虽然仍有可能,但不太可能。
将 SO_LINGER 设置为零会导致您启动异常关闭,也称为“猛烈关闭连接”。在这里,您不尊重 TIME_WAIT 并忽略流浪数据包的可能性。
如果您看到 FIN_WAIT_1,那么这可能会导致问题,因为远程位置没有发送 TCP FIN ACK 来响应您的 FIN。因此,进程要么被杀死,要么 TCP FIN ACK 由于网络分区或路由错误而丢失。
当您看到 CLOSE_WAIT 时,您遇到了问题,这里您正在泄漏连接,因为在给定 TCP FIN 时您没有发送 TCP FIN ACK。
我做了更多阅读,这是我对发生的事情的理解(希望是正确的):
当您在设置了 SO_REUSEADDR 的套接字上调用 close (或您的应用程序崩溃)时,会发生以下序列:
当您关闭 SO_LINGER 时间设置为零的套接字时:
因此,除了将 linger 设置为零是一种黑客和不良风格之外,它也是一种不礼貌的做法,因为它不会完全关闭连接。
我已经使用 SO_REUSEADDR 到通配符 bind() 到某个其他程序已经打开连接的本地端口。事实证明,只要没有两个套接字同时尝试在同一个地址/端口组合上侦听(),这种特殊用途就不会导致问题。