TCPとUDPでのデータ送信の違い(UDPの場合)

TCPとUDPとではアプリケーションデータを相手に届ける際の仕組みが異なります。今回はUDPの場合です。CでもJavaでも言語は何でも良いのですが、ここではC言語で通信を行うプログラムを作成したとします。C言語の場合、相手にデータを送る関数はsendto()を使用し、送られてきたデータを取り出す関数にはrecvfrom()を使用します。

送信側:sendto() ※データを送る。
 ↓
 IPやUDPの機能でデータが送られる。
 ↓
受信側:recvfrom() ※データを取り出す。
 
UDPがIPやイーサネットとどのように連携するのかも考えてみたいため、sendtoの送信バッファを数メガ単位で確保し、いっきにデータ送信しようとした場合で考えます。

注意:
実際はこのようなよろしくないプログラムは作らないと思いますが、そういう想定の話ということで。またソケットオプションのSO_SNDBUF、SO_RCVBUFもサイズ拡張しているという想定で。SO_SNDBUF、SO_RCVBUFはアプリケーションからOSに制御が渡ったときのOS側の送信/受信用のデータ格納領域です。

で、実際にそのような特大データをsendtoしてみると、エラーになります。UDPでは送信できるデータサイズに制限があるからです。送信できるデータサイズがいくつまでかというと65507バイトまでです。この数値がどこからきているのかということですが、以下にIPとUDPのヘッダフォーマットの図を示すのでまずは見てください。

IPヘッダにある「全パケット長」はIPパケットのサイズを表すのですが、全パケット長は16ビットであるため最大で2の16乗(つまり、65535バイト)までを格納します。このサイズはIPパケットのヘッダ長(20バイト)も含みますのでIPパケットのデータ部は65535バイトから20バイトを引いた65515バイトまでとなります。ここにUDPのデータグラムが格納されるわけですが、UDPにもヘッダがありUDPの実際のデータはもっと小さくなります。つまり、65515バイトからUDPヘッダ長(8バイト)を引いた65507バイトがUDPが一度に送信できるデータサイズとなります。なお、UDPヘッダの「長さ」のフィールドはUDPヘッダとデータ部をあわせた値が格納されます。

送信データのサイズを小さくして65507バイトのデータを送信したとしましょう。UDPはTCPと違い再送制御などの仕組みはないためアプリケーションデータはUDPからIPに引き渡されます。IPはイーサネットにデータを渡そうとするのですが、その前にMTUのチェックを行います。MTU(Maximum Trunsmission Unit)とはデータリンク層で送信できる最大データサイズのことです。イーサネットではMTUが1500バイト、光ファイバ(FDDI)は4352バイトと媒体により異なっています。アプリケーションデータがMTUのサイズを超えている場合、IPはパケットを分割してMTUのサイズ以下になるようにします。IPフラグメンテーションと言われている仕組みです。

なおフラグメントされたパケットのUDPヘッダですが、先頭のパケットのみにUDPヘッダが付与されます。残りのパケット(2個目以降のパケット)にはUDPヘッダは付与されません。経路途中にあるロードバランサがUDPヘッダの情報を見て処理をするようなケースでは、フラグメントされたパケットの処理がうまくできないので注意が必要です。

IPパケットの分割と組み立てはIPで行いますが、その際にIPヘッダにある「識別子」、「フラグ」、「フラグメントオフセット」のフィールドを利用します。IPはパケットを分割する際、フラグメンテーションしたパケットの識別子に同じID番号を割り当てます。そしてフラグを使い(厳密にはフラグの3ビット目を使い)フラグメントの途中のパケットか最後のパケットかを示します。フラグメントオフセットには元のデータのどの位置だったかを示す位置情報を格納するため、受信側でパケットを組み立てる際に利用します。

MTU以下に抑えられたパケットはイーサネットにわたり物理層のケーブルを通してネットワークに送られます。受信側にはパケットは分割されて届きますがIPがパケットの組み立てを行いUDPに引き渡します。受信側のアプリケーションプログラムは1回recvfromをすることで送られたデータを取り出すことができます。UDPでは1回の送信を1回の受信で行うことができます(パケットは分割されて到達しますが受信側で何度もrecvfromを行う必要はない、1回のrecvfromで受信できることが保証されている)。当たり前のことを書いているようですが、TCPにおいてはそうでもないのです。これについては次回書いてみようと思います。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です