如果我使用自动分配的临时端口(5000-65534)范围内的端口连接到localhost,我可以可靠地将Winsock套接字连接到 connect()
. 具体来说,Windows似乎有一个系统范围的滚动端口号,它将尝试将其指定为客户端套接字的本地端口号 . 如果我创建套接字直到分配的数字刚好低于我的目标端口号,然后重复创建套接字并尝试连接到该端口号,我通常可以让套接字连接到自己 .
我首先在一个重复尝试连接到localhost上的某个端口的应用程序中发生这种情况,当服务没有监听时,它很少成功 Build 连接并接收它最初发送的消息(恰好是Redis PING
命令) .
一个例子,在Python中(无需监听目标端口即可运行):
import socket
TARGET_PORT = 49400
def mksocket():
return socket.socket(socket.AF_INET, socket.SOCK_STREAM, socket.IPPROTO_TCP)
while True:
sock = mksocket()
sock.bind(('127.0.0.1', 0))
host, port = sock.getsockname()
if port > TARGET_PORT - 10 and port < TARGET_PORT:
break
print port
while port < TARGET_PORT:
sock = mksocket()
err = None
try:
sock.connect(('127.0.0.1', TARGET_PORT))
except socket.error, e:
err = e
host, port = sock.getsockname()
if err:
print 'Unable to connect to port %d, used local port %d: %s' % (TARGET_PORT, port, err)
else:
print 'Connected to port %d, used local port %d' (TARGET_PORT, port)
在我的Mac机器上,这最终以 Unable to connect to port 49400, used local port 49400
终止 . 在我的Windows 7计算机上,已成功 Build 连接并打印 Connected to port 49400, used local port 49400
. 生成的套接字接收发送给它的任何数据 .
这是Winsock中的一个错误吗?这是我的代码中的错误吗?
Edit: 以下是TcpView的屏幕截图,显示了违规连接:
2 回答
这似乎是_15252010_,如RFC 793的#3.4所述 . 请参见图8.请注意,在任何阶段,任何一方都不处于状态LISTEN状态 . 在您的情况下,两端都是相同的:这将导致它完全按照RFC中的描述工作 .
这是代码中的逻辑错误 .
首先,只有较新版本的Windows使用5000-65534作为临时端口 . 旧版本使用1025-5000代替 .
您正在创建多个绑定到随机临时端口的套接字,直到您绑定的套接字比目标端口小10个端口 . 但是,如果这些套接字中的任何一个碰巧实际绑定到实际目标端口,则忽略它并保持循环 . 因此,您可能或可能最终得到绑定到目标端口的套接字,并且您可能会或可能不会最终得到实际小于目标端口的最终
port
值 .之后,如果
port
恰好小于您的目标端口(无法保证),那么您在调用connect()
时会创建更多的 implicitly 绑定到不同的随机可用临时端口的套接字(如果bind()
没有,它会在内部执行隐式bind()
)已被调用),其中没有一个是你明确绑定的相同的短暂端口,因为这些端口已经在使用并且不能再次使用 .在任何情况下,您都没有任何给定的套接字从短暂端口连接到相同的临时端口 . 除非 another app 恰好绑定到您的目标端口 and 正在主动侦听该端口,否则
connect()
无法成功连接到您创建的任何套接字上的目标端口,因为它们都不在听取状态 . 并且getsockname()
在未绑定的套接字上无效,并且如果connect()
失败,则无法保证连接套接字被绑定 . 因此,根据您显示的代码,您认为正在发生的症状实际上是不可能实现的 . 您的日志记录只是做出错误的假设,因此记录错误的事情,给您一个错误的存在状态 .尝试更像这样的东西,你会看到真正的端口是什么: