1

在 Windows 上考虑以下情况:

  1. 监听套接字 1SO_EXCLUSIVEADDRUSE设置了选项,并绑定到端口
  2. 侦听套接字 1 接收传入连接,它接受该连接以创建连接的套接字
  3. 监听套接字 1 已关闭,但连接的套接字保持打开状态(在某种意义上,可能是 ESTABLISHED 或 TIME_WAIT)
  4. 监听套接字 2SO_EXCLUSIVEADDRUSE设置了选项,并尝试绑定到与第一个监听套接字相同的端口。它成功了吗?

网上没有大量关于此的信息,但大多数人同意在最后一步中,bind总是会引发错误,因为SO_EXCLUSIVEADDRUSE阻止监听套接字 2 和连接的套接字共享端口。这很重要,因为SO_EXCLUSIVEADDRUSE它提供了一些重要的安全优势,但如果它破坏了端口重新绑定,那么就很难决定是否使用它。

当前的MSDN 文档清楚地表明,至少在某些情况下,一个挥之不去的连接可能会使第二个连接bind失败,尽管他们对究竟什么是“活动连接”含糊不清:

相反,设置了 SO_EXCLUSIVEADDRUSE 的套接字不一定能在套接字关闭后立即重用。例如,如果一个设置了 SO_EXCLUSIVEADDRUSE 的侦听套接字接受了一个连接,然后随后被关闭,则另一个套接字(也具有 SO_EXCLUSIVEADDRUSE)不能绑定到与第一个套接字相同的端口,直到原始连接变为非活动状态。

libuv 明确选择不使用SO_EXCLUSIVEADDRUSE——尽管有安全优势——因为他们说它会干扰TIME_WAIT处理

这篇 microsoft.com 2005 年的博客文章提供了更多细节,声称连接的套接字SO_EXCLUSIVEADDRUSE至少在某些情况下可以防止重新绑定(尽管不是TIME_WAIT):

虽然 SO_EXCLUSIVEADDRUSE 选项非常有用,但有一个重要的警告。如果至少有一个源自或被绑定到独占访问的端口上的连接处于活动状态,则所有与该端口的绑定都将失败。在这种情况下,“连接”定义为通过 connect、WSAConnect 或 ConnectEx 显式连接到对等点的套接字,其中设置了独占标志或从侦听套接字返回的连接(例如从接受、WSAAccept 或 AcceptEx ) 具有独占选项集(在侦听套接字上)。TCP 的活动端口定义为 ESTABLISHED、FIN_WAIT、FIN_WAIT_2 或 LAST_ACK 状态

所以我写了一个小脚本来为自己尝试:

https://gist.github.com/njsmith/8770bed5bbf2154940e8e3e7762e4ac3

事实上,当我在 Windows 10 上运行它时,我得到:

rebind with existing listening socket: failed
    details: OSError(10048, 'Only one usage of each socket address (protocol/network address/port) is normally permitted', None, 10048, None)
rebind with live connected sockets: succeeded
rebind with TIME_WAIT socket: succeeded

所以我的初步结论是 MSDN 和其他所有人都错了:只要原始侦听套接字关闭,SO_EXCLUSIVEADDRUSE实际上确实允许重新绑定一个端口,该端口具有与之关联的剩余连接套接字,无论是处于 ESTABLISHED 还是 TIME_WAIT 状态或什么状态。这是每个人都想要的,所以这很好。大概这在 2005 年不是真的,但从那时到现在,他们修复了它。

问题:

  • 我对这些结果的解释是否正确?
  • 是否还有另一种情况我错过了以前的连接可能导致SO_EXCLUSIVEADDRUSE bind常规连接失败的bind情况?(我希望如果 ESTABLISHED 套接字不会引起问题,那么什么都不会,但我可能会遗漏一些东西。)
  • 最重要的是:他们什么时候做出改变的?例如,我在 Windows 10 上进行了测试,但如果我的代码出于某种原因需要针对 Windows Vista,这是否可行?还是它总是这样工作并且文档总是错误的?
4

1 回答 1

1

SO_EXCLUSIVEADDRUSE 在哪些版本的 Windows(如果有)上阻止重新绑定具有延迟 TIME_WAIT 连接的端口?

没有任何。

我对这些结果的解释是否正确?

不会。您的结果显示 ESTABLISHED 套接字不会阻止重新绑定。他们还表明 TIME_WAIT 套接字也没有,但这就是你所有的引用已经说过的。根据您引用的文档, TIME_WAIT永远不会阻止重新绑定端口。只有文档中定义的“活动端口”(ESTABLISHED、FIN_WAIT、FIN_WAIT_2 或 LAST_ACK)应该这样做,而您的脚本显示的情况并非如此,或者不再是这种情况。

您声称不同意的帖子根本不同意。

于 2017-08-11T00:20:43.033 回答