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


124

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

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

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

回答:


175

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

上のクライアント側、それは新しいアウトバウンド接続がランダムに使用するための一般的な方法でクライアント側のあなたは短時間での接続をたくさん作れば、可能なポートが不足することも可能であり、その場合には、ポートを。


2
答えてくれてありがとう、レミー!あなたの答えは私が気になったすべてのものです。;)
KJ

2
@Remy接続は、送信元/宛先ポート/ IPだけでなく、プロトコル(TCP、UDPなど)によっても区別されます。
Ondrej Peterka 2014

1
@OndraPeterka:はい、ただしすべてのプラットフォームで制限されているわけではありません。たとえば、Windowsでは、フープを介さずに同じローカルIP:ポートで個別のIPv4およびIPv6サーバーソケットをリッスンできますが、* Nixシステム(LinuxおよびAndroidを含む)ではできません。
レミールボー14

6
@ user2268997:単一のソケットを使用して複数のサーバーに接続することはできません。接続ごとに個別のソケットを作成する必要があります。
レミールボー・

3
@FernandoGonzalezSanchez:単一のクライアントは、異なるリモートIP /ポートペアに接続されている限り、同じローカルIP /ポートペアにバインドされた複数のTCPソケットを持つことができます。これはWindowsに固有のものではなく、TCPの一般的な動作方法の一部です。
Remy Lebeau

182

ポートでの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.)サーバー上で、プロセスはポートでリッスンしています。接続を取得すると、別のスレッドに渡します。通信がリスニングポートを占有することはありません。

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

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は一般的なポートであるため、大量のものがポート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.

これは、TCPソケットを開き、ペイロード(この場合は「テストペイロード」)を送信し、数秒待って切断する単純なスクリプト(https://github.com/grokit/quickweb)です。これが起こっている間に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アドレスが混同されることはありません。


11
これは私が今まで見た中で最高の答えです。
ジョブズ

1
@ N0thing「この方法では、単一のクライアントが同じ宛先ポートに対してサーバーに最大64k接続することができます。」そのため、実際には、クライアントが同じサーバーとポートに2回または複数回同時に接続しない場合、クライアントは最大64K以上の接続を持つことができます。本当。はいの場合、それはクライアント側の単一のポートから、多くの異なるサーバープロセスに接続できることを意味します(ソケット接続が異なるなど)。つまり、クライアントマシンの同じポートに複数のクライアントソケットを置くことができますか?「Remey Lebeau」の回答に対する私のコメントを読んでください。ありがとう:D
Prem KTiw

6
@premktiw:はい、複数のクライアントソケットを同じローカルIP /ポートペアに同時にバインドできます。それらが異なるサーバーIP /ポートペアに接続されているため、ローカル+リモートペアのタプルは一意です。そして、はい、クライアントが合計で64Kを超える同時接続を持つことが可能です。1つのポートから、サーバーのIP /ポートのペアが一意である限り、潜在的に無限の数のサーバー(使用可能なOSリソース、使用可能なルーターポートなどによって制限されます)に接続できます。
Remy Lebeau

1
@RemyLebeau満足しています。どうもありがとうございました:D
Prem KTiw

1
@bibsthaすべての着信接続が拒否されている場合、ファイアウォールはランダムなポートをどのように処理しますか?
PatrykG 2017年

35

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

それは一般的な直観ですが、それは正しくありません。接続されたソケットは、新しい/専用ポートに割り当てられていません。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にある「ソケットペア」の段落を参照してください。


1
完璧な答えをありがとう、ジェレミー!
KJ

6
あなたが言うことはサーバー側に完全に当てはまります。ただし、BSDソケットAPIの構造は、暗黙的にもbind()操作の前にconnect()操作が行われるため、クライアント側の発信ポートは実際には一意でなければならないことを意味します。
ローンの侯爵2012年

1
@EJPこんにちは、私bind()は以前にサーバー側でのみ使用されると思いました。accept()?クライアント側も特定のポートをバインドしますか?
GMsoF 2013

5
@GMsoF:のbind()前にクライアント側で使用できますconnect()
レミールボー2014

10

理論的にはそうです。練習ではなく。ほとんどのカーネル(Linuxを含む)ではbind()、すでに割り当てられているポートに1秒アクセスできません。これを許可するための大きなパッチではありませんでした。

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

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

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

  • ソケットは、着信パケットの宛先IPによって識別できます。たとえば、2つのIPを同時に使用するサーバーがある場合です。その後、たとえば、同じポートで異なるIP上で異なるWebサーバーを実行できます。
  • ソケットは、送信元ポートとIP でも識別できます。これは、多くのロードバランシング構成に当てはまります。

アプリケーションサーバーで作業しているので、それを行うことができます。


2
彼は秒を作ることについて尋ねませんでしたbind()
ローンの侯爵

1
@ user207421リスニングソケットが設定されていないOSを見たことがありbind()ますか?私はそれを想像することができます、はい、それはかなり可能ですが、実際には、WinSockとPosix APIの両方がそのbind()呼び出しを使用しているため、それらのパラメーター化は実質的に同じです。APIにこの呼び出しがない場合でも、どういうわけか、着信バイトをどこから読み取るかを指定する必要があります
peterh-モニカを

1
@ user207421もちろん100K以上のTCP接続が同じポートで扱うことができ、listen()/ accept()APIコールはカーネルが自分の着信ポートによってそれらを区別することを途中でソケットを作成することができます。OPの質問は、彼が本質的にそれを求めている方法で解釈できます。私はそれはかなり現実的だと思いますが、これは彼の質問が文字通り意味するものではありません。
peterh-モニカを

1

いいえ。特定の瞬間に同じポートを共有することはできません。ただし、アプリケーションを別の瞬間にポートにアクセスできるようにすることもできます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.