web-dev-qa-db-ja.com

TCP:2つの異なるソケットでポートを共有できますか?

これは非常に基本的な質問かもしれませんが、私を混乱させます。

2つの異なる接続ソケットでポートを共有できますか?私は、10万以上の同時接続を処理できるアプリケーションサーバーを作成していますが、システムで使用可能なポートの数は約60 k(16ビット)であることがわかっています。接続されたソケットは新しい(専用)ポートに割り当てられるため、複数のソケットが同じポートを共有できない限り、同時接続の数はポートの数によって制限されます。質問です。

事前に助けてくれてありがとう!

89
K J

serverソケットは単一のポートでリッスンします。そのサーバー上で確立されたすべてのクライアント接続は、同じリスニングポートに関連付けられますサーバー側接続の。確立された接続は、クライアント側とサーバー側のIP /ポートペアの組み合わせによって一意に識別されます。同じサーバー上の複数の接続は、異なるclient-side IP/Portペアに関連付けられている限り、同じserver-side IP/Portペアを共有できます。 serverは、使用可能なシステムリソースが許可する数のクライアントを処理できます。

client-sideでは、新しいアウトバウンド接続でランダムなclient-sideポートを使用するのが一般的です。短時間で多くの接続を確立します。

126
Remy Lebeau

ポートでのTCP/HTTPリスニング:多くのユーザーが同じポートを共有する方法

サーバーがTCPポートで着信接続をリッスンするとどうなりますか?たとえば、ポート80にWebサーバーがあるとします。コンピューターのパブリックIPアドレスが24.14.181.229であり、接続しようとするユーザーのIPアドレスが10.1.2.3であるとします。この人は、24.14.181.229:80へのTCPソケットを開くことであなたに接続できます。簡単です。

直感的に(そして間違って)、ほとんどの人はそれが次のように見えると仮定します:

    Local Computer    | Remote Computer
    --------------------------------
    <local_ip>:80     | <foreign_ip>:80

    ^^ not actually what happens, but this is the conceptual model a lot of people have in mind.

クライアントの観点から、彼はIPアドレスを持ち、IP:PORTでサーバーに接続するため、これは直感的です。クライアントはポート80に接続するので、ポートも80でなければなりませんか?これは考えるべき賢明なことですが、実際には何が起こるかではありません。それが正しい場合、外部IPアドレスごとに1人のユーザーしかサービスを提供できませんでした。リモートコンピューターが接続すると、ポート80からポート80への接続を独占し、他のユーザーは接続できません。

次の3つのことを理解する必要があります。

1.)サーバーでは、プロセスはポートでlisteningです。接続を取得すると、別のスレッドに渡します。通信がリスニングポートを占有することはありません。

2.)接続は、5つのタプル(ローカルIP、ローカルポート、リモートIP、リモートポート、プロトコル)によってOSによって一意に識別されます。タプル内の要素が異なる場合、これは完全に独立した接続です。

3.)クライアントがサーバーに接続するとき、ランダムな未使用の高次ソースポートを選択します。この方法では、単一のクライアントが同じ宛先ポートに対してサーバーへの最大64kの接続を確立できます。

したがって、これは実際にクライアントがサーバーに接続するときに作成されるものです。

    Local Computer   | Remote Computer           | Role
    -----------------------------------------------------------
    0.0.0.0:80       | <none>                    | LISTENING
    127.0.0.1:80     | 10.1.2.3:<random_port>    | ESTABLISHED

実際に何が起こるかを見る

まず、netstatを使用して、このコンピューターで何が起こっているのかを確認しましょう。 80の代わりにポート500を使用します(ポート80は一般的なポートであるため、すべてが大量に発生しますが、機能的には違いはありません)。

    netstat -atnp | grep -i ":500 "

予想どおり、出力は空白です。次に、Webサーバーを起動します。

    Sudo python3 -m http.server 500

次に、netstatを再度実行した場合の出力を示します。

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      - 

したがって、ポート500でアクティブにリッスンしているプロセス(状態:LISTEN)が1つあります。ローカルアドレスは0.0.0.0で、これは「すべてのIPアドレスをリッスンする」ためのコードです。簡単な間違いは、現在のコンピューターからの接続のみを受け入れるポート127.0.0.1でのみリッスンすることです。したがって、これは接続ではなく、プロセスがポートIPにbind()を要求し、そのプロセスがそのポートへのすべての接続を処理することを意味します。これは、ポートでリッスンしているコンピューターごとに1つのプロセスしか存在できないという制限を示唆しています(多重化を使用してそれを回避する方法がありますが、これははるかに複雑なトピックです)。 Webサーバーがポート80でリッスンしている場合、他のWebサーバーとそのポートを共有できません。

それでは、ユーザーをマシンに接続しましょう。

    quicknet -m tcp -t localhost:500 -p Test payload.

これは単純なスクリプト( https://github.com/grokit/quickweb )で、TCPソケットを開き、ペイロード(この場合は「テストペイロード」)を送信します。 、数秒待ってから切断します。これが発生している間に再びnetstatを実行すると、次が表示されます。

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:54240      ESTABLISHED -

別のクライアントに接続して再びnetstatを実行すると、次のように表示されます。

    Proto Recv-Q Send-Q Local Address           Foreign Address         State  
    tcp        0      0 0.0.0.0:500             0.0.0.0:*               LISTEN      -
    tcp        0      0 192.168.1.10:500        192.168.1.13:26813      ESTABLISHED -

...つまり、クライアントは接続に別のランダムなポートを使用しました。したがって、IPアドレス間に混乱はありません。

115
N0thing

接続されたソケットは、新しい(専用の)ポートに割り当てられます

それは一般的な直観ですが、間違っています。接続されたソケットは、新しい/専用ポートに割り当てられません。 TCPスタックが満たさなければならない唯一の実際の制約は、(local_address、local_port、remote_address、remote_port)のタプルが各ソケット接続に対して一意でなければならないということです。したがって、ポート上の各ソケットが異なるリモートロケーションに接続されている限り、サーバーは同じローカルポートを使用して多くのTCPソケットを持つことができます。

http://books.google.com/books?id=ptSC4LpwGA0C&lpg=PA52&dq=socket%20pair%20Tuple&pg=PA52#v=onepage&q=socket%20pair%20Tuple&f=falseの「ソケットペア」の段落を参照

27
Jeremy Friesner

理論的には、はい。練習してください。ほとんどのカーネル(linuxを含む)では、既に割り当てられたポートに2番目のbind()を許可していません。これを許可するのは、それほど大きなパッチではありませんでした。

概念的には、ソケットポートを区別する必要があります。ソケットは双方向の通信エンドポイント、つまりバイトを送受信できる「もの」です。これは概念的なものであり、「ソケット」という名前のパケットヘッダーにはそのようなフィールドはありません。

ポートは、ソケットを識別することができる識別子です。 TCPの場合、ポートは16ビット整数ですが、他のプロトコルもあります(たとえば、Unixソケットでは、「ポート」は本質的に文字列です)。

主な問題は次のとおりです。着信パケットが到着した場合、カーネルは宛先ポート番号によってソケットを識別できます。これは最も一般的な方法ですが、唯一の可能性ではありません。

  • ソケットは、着信パケットの宛先IPによって識別できます。これは、たとえば、2つのIPを同時に使用するサーバーがある場合です。次に、たとえば、同じポートで異なるIP上で異なるWebサーバーを実行できます。
  • ソケットはsource portおよびipでも識別できます。これは、多くの負荷分散構成の場合です。

アプリケーションサーバーで作業しているため、それが可能になります。

7
peterh