TCP连接通过(本地IP,本地端口,远程IP,远程端口)唯一标识。这意味着可以完全使用相同的(本地IP,本地端口)对于连接到不同远程终点的多个套接字。假设您想向“site1.com”和“site2.com”发出HTTP请求。您可以使用以下代码的套接字:
using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp)) {
socket.Bind(new IPEndPoint(IPAddress.Parse("some local ip"), 45455));
socket.Connect(server, port);
socket.Send(someBytes);
// ...
}
因此,您正在将套接字绑定到具有端口 45455 的特定本地端点。如果您现在尝试同时向“site1.com”和“site2.com”发出请求,您将收到“地址已在使用中”的异常。
但是,如果您在绑定之前添加 ReuseAddress
选项(请注意,这不是您问题所涉及的选项):
socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true)
您将能够绑定到相同的本地(IP,端口)套接字,并且在 netstat 中您将看到两个已建立连接。
所有上述都是为了表明理论上没有什么阻止使用短暂端口来建立到不同远程点的多个连接。但是,当您绑定到短暂端口(0
)时——尚不知道您要连接到哪个远程点。假设所有短暂端口都在使用中,并且您正在绑定到 0。操作系统在绑定阶段为您提供一些可重用的端口,有一个套接字正在使用此端口已连接到“site1.com”。您也正在尝试连接到“site1.com”,但这会失败(因为两个套接字的四个标识 TCP 连接的值都相同)。
SO_REUSE_UNICASTPORT
的作用是使绑定到 0 的短暂端口选择推迟到实际连接阶段(例如进行 Connect()
调用)。在这个阶段(与绑定不同),您已经知道了要连接到的本地 IP、远程 IP 和远程端口,需要选择短暂端口。假设所有端口都在使用中。现在,您可以选择连接到不同远程端点的端口来重用,而不是选择某个随机端口(可能在连接后失败)。
您可以在此MS 支持文章中进行确认:
SO_REUSE_UNICASTPORT
要实现连接方案,必须在绑定套接字之前设置套接字选项。该选项指示系统推迟端口分配,直到了解连接的 4 元组(四元组)为止。
请注意,SO_REUSE_UNICASTPORT
仅对显式绑定产生影响(如您所引用的问题摘录中所述,但仍值得重申)。如果您进行隐式绑定(例如仅 Connect()
而未进行绑定),则此选项已默认设置(在支持的情况下)。
关于此对您特定应用程序的影响。首先,从上面的内容可以清楚地看出,如果您的应用程序向同一远程端点(例如同一 HTTP 服务器)发出大量请求,则此选项不会产生影响。但是,如果您向不同的端点发出大量请求,这应该有助于防止端口耗尽。仍然,ServicePointManager.ReusePort
本身的效果取决于 HttpClient
内部如何与套接字交互。如果它只是进行 Connect()
而没有显式绑定,则此选项应该已启用(在支持的系统上)默认设置,因此将 ServicePointManager.ReusePort
设置为 true
将不会产生额外的效果;否则,它将会有所帮助。由于您不知道(也不应该依赖)其内部实现,因此在您特定的情况下启用 ServicePointManager.ReusePort
是值得考虑的。
您还可以通过限制短暂端口的范围(使用类似 netsh
System.Net.Sockets.SocketException: Only one usage of each socket address (protocol/network address/port) is normally permitted
异常,而在那个异常的机器上运行nestat -a -o
显示有许多短暂端口正在使用中。 - Allon Guralnek