web-dev-qa-db-ja.com

ソケットAPIのaccept()関数はどのように機能しますか?

ソケットAPIは、TCP/IPおよびUDP/IP通信の事実上の標準です(つまり、私たちが知っているネットワークコード)。ただし、そのコア関数の1つであるaccept()は少し魔法的です。

準形式的な定義を借用するには:

accept()はサーバー側で使用されます。リモートクライアントから新しいTCP接続を作成するために受信した着信試行を受け入れ、この接続のソケットアドレスペアに関連付けられた新しいソケットを作成します。

つまり、acceptは、サーバーが新しく接続されたクライアントと通信できる新しいソケットを返します。古いソケット(acceptが呼び出された)は同じポートで開いたままで、新しい接続をリッスンします。

acceptはどのように機能しますか?どのように実装されていますか?このトピックには多くの混乱があります。多くの人々は、受け入れが新しいポートを開き、それを介してクライアントと通信すると主張します。しかし、新しいポートは開かれていないため、これは明らかに正しくありません。実際には同じポートを介して異なるクライアントと通信できますが、どのようにですか?複数のスレッドが同じポートでrecvを呼び出す場合、データはどこに行くべきかをどのように知るのですか?

ソケット記述子に関連付けられているクライアントのアドレスの行に沿ったものであり、データがrecvを通過するたびに正しいソケットにルーティングされると思いますが、わかりません。

このメカニズムの内部の仕組みを徹底的に説明するのは素晴らしいことです。

116
Eli Bendersky

混乱は、ソケットがサーバーIP:サーバーポートによって識別されると考えることにあります。実際には、ソケットは次の4つの情報によって一意に識別されます。

Client IP : Client PortおよびServer IP : Server Port

そのため、受け入れられたすべての接続でサーバーIPとサーバーポートは一定ですが、クライアント側の情報により、すべてがどこに向かっているかを追跡できます。

物事を明確にする例:

192.168.1.1:80にサーバーがあり、2つのクライアント、10.0.0.110.0.0.2があるとします。

10.0.0.1は、ローカルポート1234で接続を開き、サーバーに接続します。サーバーには、次のように識別される1つのソケットがあります。

10.0.0.1:1234 - 192.168.1.1:80  

これで、10.0.0.2はローカルポート5678で接続を開き、サーバーに接続します。サーバーには、次のように識別される2つのソケットがあります。

10.0.0.1:1234 - 192.168.1.1:80  
10.0.0.2:5678 - 192.168.1.1:80
129
17 of 26

ユーザー「17 of 26」からの回答に追加するだけです

ソケットは、実際には5つのタプル(ソースIP、ソースポート、宛先IP、宛先ポート、プロトコル)で構成されています。ここで、プロトコルはTCPまたはUDPまたは任意のトランスポート層プロトコルです。このプロトコルは、IPデータグラムの「プロトコル」フィールドからのパケットで識別されます。

したがって、サーバー上の異なるアプリケーションが、まったく同じ4タプル上でプロトコルフィールドが異なる同じクライアントと通信する必要がある場合があります。例えば

サーバー側のApacheはTCPでserver1.com:880-client1:1234を話し、World of WarcraftはUDPでserver1.com:880-client1:1234を話します

他の4つのフィールドがすべて同じであっても、IPパケットのプロトコルフィールドが異なるため、クライアントとサーバーの両方がこれを処理します。

67
Methos

私がこれを学んでいたときに私を混乱させたのは、socketportという用語が物理的なものを示唆しているということでした。ネットワーキング。

そのため、データ構造は、異なるクライアントからの接続を分離できるように実装されます。 howが実装されている場合、答えはa。)問題ではありません。ソケットAPIの目的は、実装が重要ではないこと、またはb。)見て。 1つの実装の詳細な説明を提供する、強く推奨されるStevensの本とは別に、LinuxまたはSolarisまたはBSDのいずれかのソースをチェックしてください。

11
a2800276

他の人が言ったように、ソケットは4タプル(クライアントIP、クライアントポート、サーバーIP、サーバーポート)によって一意に識別されます。

サーバーIPで実行されているサーバープロセスは、アクティブなソケットのデータベース(どの種類のテーブル/リスト/ツリー/配列/マジックデータ構造を使用してもかまいません)を維持し、サーバーポートでリッスンします。 (サーバーのTCP/IPスタック経由で)メッセージを受信すると、データベースに対してクライアントIPとポートをチェックします。クライアントIPとクライアントポートがデータベースエントリで見つかった場合、メッセージは既存のハンドラに渡されます。それ以外の場合は、新しいデータベースエントリが作成され、そのソケットを処理するために新しいハンドラが生成されます。

ARPAnetの初期には、特定のプロトコル(FTPは1つ)が指定されたポートで接続要求をリッスンし、ハンドオフポートで応答していました。その接続のさらなる通信は、ハンドオフポートを経由します。これは、パケットごとのパフォーマンスを向上させるために行われました。当時のコンピューターは、数桁も低速でした。

1
John R Strohm