Open5
なぜポートは重複してはいけないのか
Dockerを使っていて、コンテナ同士でlocalhostで通信する場合、つまりインターフェースを共有する場合、なぜポートが重複してはいけないのでしょうか。
-
サーバーを起動したときに、
「ソケットを生成する → 指定されたポート番号をソケットに記録する(bindする)」
という動作が実行され、サーバーが指定のIPアドレスとポート番号で待ち受けることになる -
待ち受けソケットにアクセスが来ると、そのソケットは別のソケットを生成してそことクライアントを会話させる(専用窓口を作るイメージ)
-
最初に生成して待ち受けているソケットを窓口ソケットとよび、窓口ソケットからクライアントごとに生成されるソケットを専用ソケットと呼ぶことにする
-
ここで困るのがポート番号が同じソケットがいくつも作られてしまうということ
- そうするとクライアントはどこのソケットと会話すれば良いかサーバー側もクライアント側もわからなくなる
- そのため、クライアントが通信を続けるために、「クライアントのIPアドレスとポート番号、サーバー側のIPアドレスとポート番号」の4種類をセットでどのソケットかを判断できるようにする必要がある
- クライアント側はソケットごとに異なるポート番号が割り当てられるため、重複することはない
- コンテナ同士がlocalhostで通信するときは、上記4種類の情報からソケットを判断し、該当のプロセスへと辿り着く
通信の流れは
- サーバーのアプリがソケットを生成する。このとき、特定のIPアドレス(または全てのIPアドレス)とポート番号を指定する。
- ソケットはサーバーでリッスン状態になり、クライアントからの接続を待つ。
- クライアントはサーバーのIPアドレスとポート番号をもとにソケットを生成し、サーバーに接続を試みる。
- サーバーは新しい接続要求を受け入れ、その接続に対して新しいソケットを生成する。これにより、サーバーは元のソケットを使用して他のクライアントからの接続を待ち続ける一方で、新しいソケットを通じてクライアントと通信できる。
- 通信が終了すると、ソケットは閉じられ、リソースは解放される。
例えば、HTTPSの場合は
- クライアントはサーバーのIPアドレスとHTTPSのポート番号(443)を利用して、サーバーに接続を試みる。この時点で、クライアント側はランダムに選ばれた(ダイナミックポート範囲の)ポートを使用する。
- サーバー側では、HTTPSのリッスンポート(443)でクライアントからの接続要求を受け入れる。そして、サーバーとクライアント間の通信用に新しいソケットを生成する。ただし、新しいソケットもポート番号は443のまま変わらない。
- サーバーとクライアントの間で通信が行われる。この際、サーバー側のポートは443のままで、クライアント側のポートは最初にランダムに選ばれたポート番号のまま。つまり、この通信セッション全体でサーバーはポート443を、クライアントは最初に選ばれたダイナミックポートを通じて通信を行うことになる。
- 通信が終了すると、それぞれのソケットは閉じられ、リソースは解放される。
ポートが重複してはいけないとは
- サーバー側の待ち受けソケットは、サーバーのIPアドレスとポート番号の2つが重複してはいけない
- Dockerでlocalhost通信させる場合はIPアドレスが127.0.0.1(localhost)で同じになるためポートが重複してはいけない、となる
- クライアントとサーバーで通信が確立された後のソケットは、クライアントのIPアドレスとポート番号、サーバのIPアドレスとポート番号の4つ全てが一気に重複してはいけない