TCP/IP はストリーム ベースのトランスポートであり、データグラム ベースのトランスポートではありません。ストリームでは、 と の間に 1 対 1 の相関関係はありませsend()
んrecv()
。これは、データグラムにのみ当てはまります。したがって、複数の可能性を処理できるように準備する必要があります。
への 1 回の呼び出しがsend()
1 つの TCP パケットに収まり、 への 1 回の呼び出しで完全に読み取られる場合がありますrecv()
。
への 1 回の呼び出しsend()
が複数の TCP パケットにまたがる場合があり、recv()
すべてを読み取るには を複数回呼び出す必要があります。
への複数回の呼び出しがsend()
1 つの TCP パケットに収まり、 への 1 回の呼び出しで完全に読み取られる場合がありますrecv()
。
への複数の呼び出しは、send()
複数の TCP パケットにまたがる可能性があり、recv()
パケットごとに への複数の呼び出しが必要になる場合があります。
これを説明するために、 と の 2 つのメッセージが送信されているsend("hello", 5)
としsend("world", 5)
ます。を呼び出すときに可能な組み合わせを次に示しますrecv()
。
"hello" "world"
"hel" "lo" "world"
"helloworld"
"hel" "lo" "worl" "d"
"he" "llow" "or" "ld"
アイデアを得ますか?これは単に TCP/IP がどのように機能するかです。すべての TCP/IP 実装は、この断片化を考慮する必要があります。
データを適切に受信するには、 への個々の呼び出しではなく、論理メッセージを明確に分離する必要があります。これは、単一のメッセージを送信するために を複数回send()
呼び出す必要があり、単一のメッセージを完全に受信するために複数回呼び出す必要があるためです。したがって、前の例を考慮して、メッセージ間にセパレーターを追加しましょう。send()
recv()
send("hello\n", 6);
send("world", 5);
send("\n", 1);
recv()
受信側では、文字が受信されるまで何度でも呼び出し\n
、その文字に至るまで受信したすべてを処理します。終了時に読み取りデータが残っている場合は、後で処理するために保存しrecv()
、次の\n
文字まで呼び出しを再開します。
メッセージ間に一意の文字を配置できない場合があります (メッセージ本文ですべての文字を使用できるため、区切り文字として使用できる明確な文字がない場合があります)。その場合、前の整数、構造化ヘッダーなどとして、代わりにメッセージの長さをメッセージの前に付ける必要があります。次にrecv()
、完全な整数/ヘッダーを受け取るまで、必要な回数だけ呼び出してから、recv()
長さ/ヘッダーが指定するのと同じ数のバイトを読み取るために必要な回数だけ。終了したら、必要に応じて残りのデータを保存し、最初から呼び出しを開始recv()
して、次のメッセージの長さ/ヘッダーなどを読み取ります。