【发布时间】:2011-03-09 01:35:44
【问题描述】:
我正在阅读有关使用 SO_LINGER 套接字选项通过将逗留时间设置为零来故意“暗杀”时间等待状态的信息。这本书的作者接着说我们永远不应该这样做,一般来说我们永远不应该干涉时间等待状态。然后他立即建议使用 SO_REUSEADDR 选项绕过时间等待状态。
我的问题是,有什么区别?在这两种情况下,您都过早地终止了时间等待状态并冒着接收重复段的风险。为什么一个好一个坏?
【问题讨论】:
我正在阅读有关使用 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 并不意味着忽略杂散的 TCP 数据包。通常,它用于在连接(或 TIME_WAIT 中的旧连接)仍然存在时将 TCP 侦听套接字绑定到端口。如果一个进程侦听,然后产生一个子进程来处理传入连接,然后父进程在连接仍然存在时退出或死亡,就会出现这种情况。为了能够重新启动父级,需要SO_REUSEADDR,这是非常安全的。请阅读 Stevens、Fenner 和 Rudoff(ISBN 0-13-14115-1)第 210-213 页的“UNIX 网络编程,第三版”中的更多内容。
我做了更多阅读,这是我对发生的事情的理解(希望是正确的):
当您在设置了 SO_REUSEADDR 的套接字上调用 close(或您的应用程序崩溃)时,会发生以下顺序:
当您关闭 SO_LINGER 时间设置为零的套接字时:
因此,除了将 linger 设置为零是一种 hack 和糟糕的风格之外,它也是一种不礼貌的行为,因为它不会彻底关闭连接。
【讨论】:
SO_REUSEADDR 不会阻止 TIME_WAITs。它只允许你 bind() 一个新的套接字(例如,在调用 listen() 之前)到一个已经在使用的地址/端口组合。这样做有很多很好的理由——太多的理由不适合发表评论。请阅读 Stevens、Fenner 和 Rudoff(ISBN 0-13-14115-1)第 210-213 页的“UNIX 网络编程,第三版”中的更多内容。
我已经使用 SO_REUSEADDR 将通配符 bind() 绑定到本地端口,其他程序已经为其打开了连接。事实证明,只要没有两个套接字同时尝试在同一个地址/端口组合上侦听(),这种特殊用途就不会导致问题。
【讨论】: