6

connect()如果我使用自动分配的临时端口(5000-65534)范围内的端口连接到本地主机,我可以可靠地获得一个 Winsock 套接字。具体来说,Windows 似乎有一个系统范围的滚动端口号,这是它将尝试分配为客户端套接字的本地端口号的下一个端口。如果我创建套接字直到分配的数字刚好低于我的目标端口号,然后重复创建一个套接字并尝试连接到该端口号,我通常可以让套接字连接到自己。

我首先让它发生在一个反复尝试连接到本地主机上的某个端口的应用程序中,当服务没有监听它时,它很少成功建立连接并接收它最初发送的消息(恰好是一个 RedisPING命令)。

一个例子,在 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 机器上,成功建立连接并打印Connected to port 49400, used local port 49400. 生成的套接字接收发送给它的任何数据。

这是 Winsock 中的错误吗?这是我的代码中的错误吗?

编辑:这是TcpView的屏幕截图,其中显示了有问题的连接:

python.exe 8108 TCP (我的主机名) 49400 localhost 49400 已建立

4

2 回答 2

2

这似乎是RFC 793 #3.4 中所述的“同时启动” 。请参见图 8。请注意,任何一方都不会在任何阶段处于 LISTEN 状态。在您的情况下,两端是相同的:这将导致它完全按照 RFC 中的描述工作。

于 2013-07-13T06:11:18.833 回答
0

这是您代码中的逻辑错误。

首先,只有较新版本的 Windows 使用 5000–65534 作为临时端口。旧版本改为使用 1025-5000。

您正在创建多个明确绑定到随机临时端口的套接字,直到您绑定的套接字比目标端口少 10 个端口。但是,如果这些套接字中的任何一个碰巧实际绑定到实际的目标端口,您将忽略它并继续循环。port因此,您可能会或可能最终得到一个绑定到目标端口的套接字,并且最终可能会或可能不会得到实际上小于目标端口的最终值。

之后,如果port恰好小于您的目标端口(不能保证),那么您将创建更多套接字,这些套接字在调用时隐式绑定到不同的随机可用临时端口(如果尚未调用connect(),它会在bind()内部进行隐式调用) bind(),它们都不是您明确绑定到的相同临时端口,因为这些端口已在使用中且无法再次使用。

在任何时候,您都没有任何给定的套接字从一个临时端口连接到同一个临时端口。除非另一个应用程序碰巧将自己绑定到您的目标端口正在积极地侦听该端口,否则connect()无法成功连接到您创建的任何套接字上的目标端口,因为它们都不在聆听状态。并且getsockname()在未绑定的套接字上无效,如果connect()失败,则不保证连接的套接字。因此,鉴于您显示的代码,您认为正在发生的症状实际上在物理上是不可能的。您的日志记录只是做出错误的假设,因此记录了错误的事物,从而使您处于错误的存在状态。

试试类似这样的东西,你会看到真正的端口是什么:

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()
    print 'Bound to local port %d' % (port)
    if port > TARGET_PORT - 10 and port < TARGET_PORT:
        break

if port >= TARGET_PORT:
    print 'Bound port %d exceeded target port %d' % (port, TARGET_PORT)
else:
    while port < TARGET_PORT:
      sock = mksocket()
      # connect() would do this internal anyway, so this is just to ensure a port is available for logging even if connect() fails
      sock.bind(('127.0.0.1', 0))
      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 using local port %d' % (TARGET_PORT, port)
      else:
          print 'Connected to port %d using local port %d' % (TARGET_PORT, port)
于 2013-07-11T04:27:35.387 回答