6

connect()自動的に割り当てられたエフェメラル ポート (5000 ~ 65534) の範囲内のポートを使用して localhost に接続すると、Winsock ソケットを確実に取得できます。具体的には、Windows は、クライアント ソケットのローカル ポート番号として割り当てようとする次のポートであるシステム全体のローリング ポート番号を持っているようです。割り当てられた番号がターゲット ポート番号のすぐ下になるまでソケットを作成し、ソケットを繰り返し作成してそのポート番号に接続しようとすると、通常はソケットをそれ自体に接続できます。

最初に、localhost の特定のポートに繰り返し接続しようとするアプリケーションで発生しました。サービスがリッスンしていない場合、接続を正常に確立し、最初に送信したメッセージを受信することはほとんどありません (たまたま 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 マシンでは、接続が正常に確立され、出力されConnected to port 49400, used local port 49400ます。結果のソケットは、送信されたすべてのデータを受信します。

これは Winsock のバグですか? これは私のコードのバグですか?

編集:これは、問題のある接続が表示されたTcpViewのスクリーンショットです。

python.exe 8108 TCP (私のホスト名) 49400 localhost 49400 ESTABLISHED

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()、作成したソケットのいずれかでターゲット ポートに正常に接続する方法はありません。リスニング状態。Andgetsockname()は、バインドされていないソケットでは無効であり、失敗した場合、接続しているソケットがバインドされることは保証されませ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 に答える