在 Windows 中保留 TCP 端口
我想保留一个 TCP 端口,以便稍后由服务绑定,以便 Windows 在分配随机端口号时不会无意中使用相同的端口号。我知道这可以通过注册表和重新启动来实现,但我想避免这种严厉的解决方案。
进程如何在不实际绑定/监听端口的情况下保留端口,然后根据请求安全地(即避免竞争条件)将其移交给另一个进程?
端口号无需提前确定。第一个进程可以获取随机端口号,并将其传递给请求进程。
编辑:我发现我的问题表述得有些糟糕。我真正想要的是将动态端口号的分配与绑定到端口零操作分开。这意味着不仅可以避免意外随机分配该端口号,还可以防止任何其他进程在此期间绑定到同一地址/端口。或者,换句话说,我希望一个进程启动绑定到端口零操作 - 立即了解将使用的端口号 - 并让指定的第二个进程在将来的某个时候完成绑定操作。
目前,我能想到的最接近的解决方法是第一个进程立即绑定到地址/0,并保持绑定直到第二个进程请求它,此时它解除绑定并告诉另一个进程它的端口号获取,然后显式绑定到地址/端口。这有两个问题:1)我宁愿在第二个进程出现之前根本不绑定; 2) 在一段很短的时间间隔内,第三方可能会意外(或故意)篡夺端口。
背景
你可能会好奇为什么我想做一些如此奇怪的事情。我一直在尝试 ZeroMQ,一个主要限制是 Windows 上缺少 ipc://
传输。我突然想到,端口映射器进程(类似于 RPC 端点映射器或 Erlang 的 epmd)正是使用 tcp:// 传输和动态端口分配来实现解决方案的门票。然而,ZeroMQ 客户端和服务器允许无序连接(即,客户端在服务器绑定之前连接不是错误),所以我试图弄清楚连接客户端如何发现 - 用一个非常高度确定性 - 在服务器实际绑定到该端口之前将用于通信的端口。
I'd like to reserve a TCP port, to be bound by a service later, so that Windows doesn't inadvertently use the same number when assigning random port numbers. I know this is possible via the registry and a reboot, but I would like to avoid such a heavy-handed solution.
How can a process reserve a port without actually binding/listening to it, and then safely (i.e., avoiding race-conditions) hand it over to another process on request?
The port number needn't be determined in advance. It's OK for the first process to acquire a random port number, and pass it to the requesting process.
EDIT: It occurs to me that my question is somewhat poorly stated. What I really want is to separate the allocation of a dynamic port number from the bind-to-port-zero operation. This means not just avoiding accidental random allocation of that port number, but also preventing any other process from binding to the same address/port in the interim. Or, putting it another way, I want one process to start the bind-to-port-zero operation — immediately learning the port number that will be used — and let a nominated second process complete the bind operation sometime in the future.
At the moment, the closest work-around I can think of is for the first process to bind to address/0 immediately, and stay bound until the second process requests it, at which point it unbinds and tells the other process the port number it acquired, which then binds to the address/port explicitly. This has two problems: 1) I'd rather not bind at all until the second process comes along; 2) there's a small time interval during which a third party could accidentally (or deliberately) usurp the port.
Background
You may be curious as to why I wish to do something so odd. I've been toying with ZeroMQ, and one major limitation is the absence of the ipc://
transport on Windows. It struck me that a port mapper process (akin to the RPC endpoint mapper, or Erlang's epmd) would be just the ticket to implement a work-around using the tcp://
transport with dynamic port allocations. However, ZeroMQ clients and servers are allowed to connect out of order (i.e., it isn't an error for the client to connect before the server binds), so I am trying to figure out how a connecting client can discover — with a very high degree of certainty — the port that will be used to communicate, before a server actually binds to that port.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
正如 @vahapt 所提到的,您可以使用 netsh 修改动态端口范围。
但是,更好的解决方案可能是使用 netsh 保留应用程序所需的端口,而不保留动态端口的默认范围。
为此:
停止使用要保留的端口的任何进程。如果进程正在使用要保留的端口范围内的端口,NETSH 将返回以下错误并且保留将失败:
<块引用>
该进程无法访问该文件,因为该文件正在被另一个进程使用。
使用以下 NETSH 命令保留端口:
netsh int;添加排除端口范围 [protocol=]tcp|udp [startport=][端口数=]<整数> [[store=]活动|持久]
例如,要为 UDPv6 保留端口 55368-55372,请使用以下命令:
netsh int ipv6 add exceptedportrange protocol=udp startport=55368 numberofports=5
注意:
请参阅https://support.microsoft.com/en-us/kb/929851了解更多信息信息,包括如何查看或删除现有端口预留。
As mentioned by @vahapt you can modify the dynamic port range using
netsh
.However, a better solution may be to use netsh to reserve the ports required by your application, leaving alone the default range of dynamic ports.
To do so:
Stop any processes using the ports to be reserved. If a process is using a port included in the range of ports to be reserved, NETSH will return the following error and the reservation will fail:
Use the following NETSH command to reserve the ports:
netsh int <ipv4|ipv6> Add excludedportrange [protocol=]tcp|udp [startport=]<integer> [numberofports=]<integer> [[store=]active|persistent]
For example, to reserve ports 55368-55372 for UDPv6, use the command:
netsh int ipv6 add excludedportrange protocol=udp startport=55368 numberofports=5
Notes:
See https://support.microsoft.com/en-us/kb/929851 for more information, including how to view or delete existing port reservations.
使用 netsh 命令可能会对您有所帮助。您可以更改 Windows 使用的动态端口范围。
就像您所指示的注册表修改一样,但它是立即生效的。
有关 netsh 命令的详细信息,请参阅:http://support.microsoft.com/kb/929851。
Using netsh command might help you. You can change the dynamic port range used by Windows.
It is like the registry modification that you indicated, but it is effective immediately.
see: http://support.microsoft.com/kb/929851 for details about netsh command.
编辑:这仅适用于 Windows Server 2008 之前的版本 (Microsoft 支持知识库)
中的“ReservedPorts”注册表设置
您可以编辑HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
要保留一系列端口,请遵循格式“4000-4010”或“xxxx-yyyy”保留一个端口,您必须使用“4000-4000”或“xxxx-xxxx”的格式
http: //support.microsoft.com/kb/812873
Edit: This only applies to pre-Windows Server 2008 (Microsoft Support KB)
You can edit the 'ReservedPorts' Registry Setting in
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters
To reserve a range of ports follow the format '4000-4010' or 'xxxx-yyyy' however to reserve a single port you have to use the format of '4000-4000' or 'xxxx-xxxx'
http://support.microsoft.com/kb/812873
我已经想出了一个可能的解决方案,所以我想我也可以在这里记录它作为答案。
进程可以通过调用 WSADuplicateSocket 将套接字传递给另一个进程,因此协调进程可以绑定到动态端口,并在内部将其与给定的 IPC 名称关联起来。当想要“绑定”到该名称的 ZMQ 服务器进程到达时,协调进程会将绑定的套接字复制到服务器进程并关闭自己的副本。
这个解决方案并没有解决我避免调用bind()的偏好,但这可能不是绝对必要的;我必须进行一些测试。
I've come up with a possible solution, so I thought I may as well document it here as an answer.
A process can pass a socket over to another process via a call to WSADuplicateSocket, so a coordinating process could bind to a dynamic port, and internally associate it with a given IPC name. When a ZMQ server process wanting to "bind" to that name arrives, the coordinating process copies the bound socket to the server process and closes its own copy.
This solution doesn't address my preference to avoid calling bind(), but that may not be strictly necessary; I'll have to perform some tests.
对于 ZeromMQ,您可以使用 czmq 或 C# NetMq 中的
zbeacon
模块来实现服务发现。For ZeromMQ, you can use the
zbeacon
module from czmq or C# NetMq to implement service discovery.