UDP を使ってみよう (5)

前へ << UDP を使ってみよう (3) DNS クライアントを作ってみよう (1) >> 次へ

TCP はストリーム指向

TCP は connect を呼び、接続先に対して一本のしっかりとしたコネクションを張り、 そのコネクション上にデータを流すというイメージでした。TCP で
print SOCKET "data1";
print SOCKET "data2";
としたとき、data1 は data2 より必ず先に届きます。 多くの場合、送信側 OS が
"data1data2"
と結合してから、1つのパケットとして送信します。 もし
"data1" "data2"
という2つのパケットで送信して、 仮に送信途中で順序が入れ替わり、
"data2" "data1"
という順序で相手先に到着したとしても、 受信側 OS がパケットの送信順序を調べ、 サーバプログラムがデータを読み込むときには
"data1" "data2"
という順序に入れ替わっています。つまり、
1. print SOCKET "data1";
   print SOCKET "data2";
2. print SOCKET "data1data2";
3. print SOCKET "dat";
   print SOCKET "a1d";
   print SOCKET "ata2";
送信側が 1〜3 のどの書き方をしても、データ受信側からすると全く等価なのです。 これを
TCP はストリーム指向
と表現します。1本のコネクション上をデータが流れていくので、 順序は狂いませんし、送信のための API を何回呼んだかは問題ではありません。 どういうデータが送られたかが重要なのです。

UDP は「非」ストリーム指向

しかし、UDP の場合は「コネクション」という概念はありません。 相手側に小さなボールを何個も投げつけるようなものです。 よって、send を呼び出した数だけパケットが送信されます。

a. send(SOCKET, "data1", 0, $sock_addr);
   send(SOCKET, "data2", 0, $sock_addr);
b. send(SOCKET, "data1data2", 0, $sock_addr);
は全く違います。a はパケットが必ず 2個流れます。b だと 1個です。
ここでは「パケット」という曖昧な書き方をしていますが、 正確には「UDP データグラム」です。 ただし、MTU の値が著しく小さい場合、b のような書き方でも IP の段階で分割され、複数の IP データグラムとして送信される可能性はあります。 ただし MTU は必ず 576 バイト以上であるべしと規定されていますので、 "data1data2" のような短いデータでは分割はされないでしょう。
また、a の書き方では、受信側に "data1" の方が "data2" より先に届くという保証はありません。順序は逆転しているかもしれません。 受信側では "data1" と "data2" のどちらが先に送信されたものかを知ることはできません。

ストリーム指向かどうかで TCP か UDP かを選択すべし

ストリーム指向なプロトコルならば TCP が適当で、 非ストリーム指向なプロトコルならば UDP が向いていると言えます。

例えば、FTP のようなファイル転送を行なう場合、 1つの大きなファイルを小さなパケットに分割して送らなければいけません。 どのパケットが1番目で、どのパケットが2番目なのか、順序関係を明確にしないといけませんので、 TCP が向いています。

一方、DNS のように 1つ1つの問い合わせが独立しており、 それらの間には順序関係がない場合は UDP が向いていると言えます。

前へ << UDP を使ってみよう (3) DNS クライアントを作ってみよう (1) >> 次へ