如何检查TCP端口是否已被监听?

5
我有一个第三方库,它充当HTTP服务器。我传递给它一个地址和端口,然后它使用它们来监听传入的连接。这个库以这样的方式监听,以至于它不会对它绑定的端口和地址进行独占式使用。因此,我可以在同一端口上多次监听。
我需要在同一进程中运行多个此HTTP服务器实例。每个实例都有一个默认端口,但如果该端口不可用,则应使用下一个可用端口。这就是我的问题所在;我可能会得到两个正在侦听同一端口的HTTP服务器。
我无法更改HTTP服务器的代码,并且HTTP服务器在无法侦听我提供的端口时不会通知我,因此我必须能够在启动每个HTTP服务器之前检查端口是否已被使用。我尝试通过将自己的套接字绑定到SO_REUSEADDR设置为FALSE和SO_EXCLUSIVEADDRUSE设置为TRUE来检查某个端口是否已被监听,但是当现有的HTTP服务器已在该端口上侦听时,bind和listen调用都会成功。
这个HTTP服务器是如何实现这种效果的,我如何准确地检查是否以这种方式侦听某个端口?

你必须使用这个特定的HTTP服务器库吗?考虑到它已经损坏,而且你无法控制它或者甚至无法访问以弄清楚它的工作原理,更换一个似乎是一个非常好的主意。 - abarnert
好奇一下,是哪个库? - James M
这个库的监听方式不会独占绑定的端口和地址。在TCP中是不可能的。TCP中唯一可能的端口共享形式是绑定到不同的本地IP地址和相同的端口号。您需要重新调查并重新表述您的问题。 - user207421
@EJP:如果不可能,那么这是怎么发生的?http://i.imgur.com/7ukBb.png我可以重现这个问题,无论我绑定到什么地址。 - Collin Dauphinee
在Windows上,两个TCP套接字绑定同一个端口是完全可能的。这并不是“有用”的(文档明确指出其效果是未定义和不确定的),但它确实会发生。(而且听起来OP试图防止它发生,因为它没有用……虽然我承认我对他的确切意图仍有些不清楚。) - abarnert
3个回答

6

一种快速而简单的方法是尝试在本地主机上使用connect()连接端口。如果connect()调用成功,则知道该端口当前正在被监听(由接收连接的人)。如果连接调用失败(特别是出现ECONNREFUSED),则可以相当确定没有任何人在监听该端口。

当然,这里存在竞争条件:实际上没有阻止另一个程序在您运行上述测试之后但在您自己绑定到端口之前立即抢占该端口。因此,您应将测试结果视为提示而非绝对规则,并且(希望)有某种处理方式,以便在稍后发现端口实际上正在使用时进行处理。


他打算的测试代码将具有完全相同的竞态条件(或者更确切地说,如果它工作的话将具有相同的竞态条件...)。实际上,鉴于他的设计,没有任何避免这种情况的方法。 - abarnert
(以上是解释为什么我给了您+1,尽管存在竞争条件,而不是反对您的答案或其他任何事情。) - abarnert
bind 怎么样?如果 bind 在那个端口失败了,那么它就没有被绑定。 - zwcloud

3

使用端口号0,操作系统会自动选择一个空闲端口。


我无法访问HTTP服务器的内部套接字,因此如果我将0作为其端口传递,则无法获取操作系统分配的端口。 - Collin Dauphinee
然后使用端口0绑定一个套接字,使用getsockaddr发现为您选择的端口号,使用相同的端口初始化HTTP服务器,然后关闭临时套接字。 - James Youngman

1

http://msdn.microsoft.com/en-us/library/windows/desktop/ms740621(v=vs.85).aspx解释了不同选项的交互方式。

你没有给我们足够的信息来告诉我们你的使用情况,但我可以通过一个任意的使用情况来解决你所看到的问题。

假设你正在Win 2003或更高版本上运行,你的主要NIC是10.0.0.1,并且所有内容都在同一个用户帐户下运行。

你的应用程序的第一个实例启动,你的测试代码尝试使用SO_EXCLUSIVEADDREUSE绑定10.0.0.1:12345。当然这个是有效的。

你关闭套接字,然后告诉HTTP服务器监听端口12345。它使用SO_REUSEADDR绑定0.0.0.0:12345,这当然是有效的。

现在你的应用程序的第二个实例启动,你的测试代码尝试使用SO_EXCLUSIVEADDREUSE绑定10.0.0.1:12345。根据MSDN文章中的图表,这是有效的。

你关闭套接字,然后告诉HTTP服务器监听端口12345。它使用SO_REUSEADDR绑定0.0.0.0:12345,这个方法是有效的。

如果这是问题所在,假设你无法让HTTP服务器绑定到特定地址,你可以在测试代码中使用0.0.0.0来解决问题。(当然,如果是其他数百种可能的问题之一,这个解决方案就不起作用了。)

如果你不知道HTTP服务器正在使用哪些套接字选项、地址等,并且没有源代码,只需在调试器中运行它并断点相关调用即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接