TCPとUDPの本質を押さえる

 Part1 アプリケーションに欠かせぬ通信のプラットフォーム

2007/03/12
斉藤 栄太郎=日経NETWORK

ネットワークはLANやIPだけじゃない。相手との通信を成り立たせるには,TCPやUDPが不可欠だ。むしろ,Webブラウザや電子メール・ソフトなどのネットワーク・アプリケーションにとって,TCPとUDPはなくてはならない基盤的な存在である。Part1では,TCPとUDPの概要を示す。

 インターネットなどTCP/IPを使ったネットワークでは,データをIPパケットという単位でやりとりする。このIPパケットには,通信相手の住所を表すIPアドレスが書かれており,ルーターがこのIPアドレスを見て転送する。ここまでは,知っている人が多いだろう。

 しかし,これだけでは通信は成り立たない。Webアクセスやメールの送受信など,ほどんどのインターネット・アプリケーションが動かないのだ。

 その理由の一つは,IPアドレスは単に相手のコンピュータを特定するだけだからだ。パケットを受信してもコンピュータはそのデータをどのアプリケーション(サーバー・ソフト)に渡せばいいか判断できない。つまり,住所で,家や会社までは荷物が届くが,氏名や部署名がないとだれに荷物を渡せばいいかわからないというわけだ。

 またルーターは,配送の途中でIPパケットがなくなろうが,遅れて届こうがまったく気にしない。それどころか,一度に大量のIPパケットを受信すると,処理しきれずにパケットを捨ててしまうことさえある。

 もし,こんな信用できない宅配業者があったら,そのままではだれも使わないだろう。きちんと配達してくれるほかの業者を選ぶか,携帯電話などを使って相手に荷物が届いたかを確認するはずだ。

 IPパケットを使って通信する場合も,氏名や部署名に相当する情報から適切なアプリケーションへデータを渡したり,送達確認などをしてくれる仲介役が必要になる。それが「TCP」と「UDP」だ(図1)。これらのプロトコルのおかげで,Webブラウザ,電子メール・ソフト,FTPクライアントなどが動く。

図1●インターネット・アプリケーションはTCPかUDPがないと動かない
 
[画像のクリックで拡大表示]

TCPとUDPは用途が違う

 TCPとUDPはアプリケーションとIPパケットの仲介役という点では同じだが,アプリケーションに提供する機能は大きく違う。

 TCPは,日常的に利用するWebブラウザや電子メール・ソフト,FTPクライアントなどが使い,送達確認や送受信スピードの調整をする。したがって,TCPを利用するアプリケーションは,相手にデータが届いたかどうかといったことを気にしないで済む。

 一方のUDPは,TCPが持っている送達確認やスピード調整機能などをバッサリと捨て,アプリケーションにほとんどの処理を任せる。こうした機能が必要な場合はアプリケーションが独自に用意する必要があるが,これらの処理をアプリケーションが好き勝手に追加・削除できるという利点もある。代表的なアプリケーションは,ドメイン名からIPアドレスを調べるときなどに使うDNS,クライアントにIPアドレスなどを自動的に割り当てるDHCP,ネットワーク機器をリモートから管理するためのSNMPなどがある。

実際は教科書とは違う

 実は,TCPやUDPの詳細を記述した解説書は,ちまたにあふれかえっている。大もとの技術文書であるRFCもある。このRFCを読めば基本的なしくみはすべてわかるようになっている。

 ただ,こうしたRFCや解説書には,世の中で動いているTCPやUDPの実際の動きが書かれていない。TCPやUDPは20年以上も前に決められたため,今では使っていない機能があったりするのだ。また,RFCには,TCPの通信状態を『状態遷移図』という流れ図で示しているが,実際にこれを基にTCPプログラムを開発しようとすると,ある状態でこうなったらどうするかという,例外的な処理がキチンと書かれていなかったりする。つまり,RFCの仕様にも,思いがけない抜け穴があったりするのだ。このため,実際のTCP/IPソフトはOSなどによって,パラメータやオプションの扱いで違う部分がある。

 そこで,TCP編UDP編に構成を分け,それぞれの基本を押さえながら,実際の動きを探っていこう。TCP編では,通信相手のアプリケーションへデータを確実かつ効率よく届けるしくみを追っていく。そうすれば,ブロードバンド・ルーターやファイアウォールの設定で登場してくる「ポート番号」やフィルタリングの意味が理解できるようになるだろう。さらに,「ブロードバンド・サービスを導入したのに,FTPでのダウンロード速度が思っていたほど速くならない」,「Webブラウザを一度に複数立ち上げてもそれぞれが正しくデータを受信してページを表示できるのはなぜか」――といった理由もわかる。

 それに続くUDP編では,TCPにはある機能が,UDPでなぜ削られているのかを考え,UDPを使うアプリケーションの姿を浮き彫りにする。

 


Part2 TCP編–接続と切断,制御,仲介,確実かつ効率よく届ける

2007/03/13
斉藤 栄太郎=日経NETWORK

相手のアプリケーションに正確なデータを確実かつ効率よく届けるために,TCPの役割は「接続と切断」,「通信制御」,「アプリケーションとの仲介」という三つの機能に分かれている。Part2では,これら三つの機能を順番に探っていくことにしよう。

 TCPの役割は,相手のアプリケーションに正確なデータを確実かつ効率よく届けることだ。もう少し細かく見ると,「接続と切断」,「通信制御」,「アプリケーションとの仲介」という三つの機能に分かれている(図1)。

図1●TCPによる通信を三つの役割に分けて考えよう
 
[画像のクリックで拡大表示]

 「接続と切断」とは,通信相手を呼び出して相手と1対1で通信できる仮想的な通信路を確保すること。通信路は「コネクション」と呼ばれ,ほかの通信にじゃまされずに相手との間で,データを確実にキャッチボールできるようにする。このコネクションを確立する手順が「3WAY(スリーウェイ)ハンドシェーク」である。通信相手を呼び出して,これからキャッチボールを始めるという“あいさつ”を交わすイメージだ。

 「通信制御」とは,通信相手とあいさつを交わしたあとのキャッチボールを確実かつ効率よくできるようにするしくみである。具体的には,送受信するデータ量や送出スピードをコントロールして相手が確実に受信できるようにしたり,通信途中で紛失したデータを再送したりする。

 TCPのもう一つの役割が,「アプリケーションとの仲介」である。「ポート番号」という識別番号や「ソケット」と呼ぶしくみを使って,適切なアプリケーションに受信データを受け渡す。

 では,これら三つの機能を順番に探っていくことにしよう。

役割1 接続と切断
データ転送に先立って3回“あいさつ”を交わす

 TCPに限らず,どんな通信プロトコルでも,データをどういう単位でやりとりし,その制御にどんな情報を使うのかという「プロトコル・フォーマット」を理解することは重要だ。役割1では,TCPのプロトコル・フォーマットを確認するところから始めよう。

IPパケットに収まるTCPセグメント

 まずは,自分のコンピュータからインターネットにあるサーバーへTCPを使ってアクセスするときのことを考えよう(図2)。

図2●TCPはIPパケットのデータ部分に格納される
 
[画像のクリックで拡大表示]

 インターネットなどでは,「IPパケット」を基本単位としてやりとりする。しかし,コンピュータはIPパケットをそのまま送り出すわけではない。イーサネットなどとつながっているからだ。この場合IPパケットは,イーサネットで使うMAC(マック)フレームのデータ部分に入る。これと同様に,TCPは,IPパケットのデータ部分に収められる。さらに,このTCPのデータ部分には,Webブラウザなどのアプリケーションが作成したメッセージなどが入る。つまり,アプリケーションが作成したデータは,さまざまなプロトコルによって何重にもくるまれて運ばれる。

 各プロトコルで扱うデータの固まりの呼び方も確認しておこう。イーサネットでは「MACフレーム」,IPでは「IPパケット」(あるいはIPデータグラム),TCPでは「TCPセグメント」と呼ぶ

最大データ量には制限がある

 TCP/IPの階層構造に関してもう一つ覚えておきたいのは,上位プロトコルのデータ・サイズは,下位プロトコルのデータ・サイズに制限されるという点だ。

 例えば,下がイーサネットなら,中に入るIPパケットの最大サイズはMACフレームのデータ部分の最大サイズ(1500バイト)になる。同様に,そのIPパケットの中に入るTCPセグメントの最大サイズは,IPパケットのヘッダー分(最小で20バイト)を除いたサイズになる。

 TCP/IPの専門用語では,この1回に運べるIPパケットの最大サイズのことをMTU,TCPセグメントのデータ部分の最大サイズのことをMSSと呼ぶ。特にMSSの方は,このあとでしばしば登場するので覚えておこう。

ミニ解説1
IPの上位はTCPとUDPだけじゃない
 TCPとUDPは,IPの上位プロトコルとしてあまりにも有名だが,実はIPの上位プロトコルはほかにもある。上位プロトコルはIPヘッダーの「プロトコル・フィールド」で指定するが,このフィールドは8ビット分用意されているので,256種類まで登録できる。もちろん,実際にはそんなにないが,ほとんど無名なものまで含めると,ほぼ100番までが予約されている。比較的有名なプロトコルとしては,ルーター同士が経路情報を交換するために使うOSPF(open shortest path first)がある。

すべての制御情報はヘッダーにある

 前置きが少し長くなってしまったが,TCPセグメントの中身に話を移そう。

 TCPセグメントは,ヘッダー部分とデータ部分に分かれる(図3)。重要なのはヘッダー部分で,通信制御などに使う情報がすべてここに入る。サイズは通常20バイトで,オプションを使うと最大60バイトになる。

図3●TCPのプロトコル・フォーマットと主なパラメータの意味
 
[画像のクリックで拡大表示]

 一方のデータ部分には,TCPを利用するアプリケーションのデータが入る。具体的には,Webアクセスで使うHTTPのリクエスト・メッセージや,電子メールの送受信データなどが格納される。TCPは,このデータ部分が壊れていないかを確認するが,中身についてはいっさい関知しない。

制御ビットで相手に状況を伝える

 ヘッダー部分をもう少しじっくり見てみると,通信相手のアプリケーションを識別するために使うポート番号や,送信データの順番などを確認するために使う「シーケンス番号」など,たくさんのフィールドがある。このうち,この役割1で押さえておきたいのが「制御ビット」のフィールドである。

 制御ビットのフィードはTCPセグメントの先頭から数えて107ビット目から始まり,1ビットごとにその領域に名前が付いている。それは順番に,(1)URG(urgent:緊急確認/アージェント),(2)ACK(acknowledgement:確認応答/アック),(3)PSH(push:プッシュ),(4)RST(reset:リセット),(5)SYN(synchronize:同期/シン),(6)FIN(finish:終了/フィン)--と呼ばれる。それぞれの領域には1か0が入り,1ならば「フラグ(旗)が立つ」という。例えば,ACK領域が1だと「ACKフラグが立っている」あるいは「ACKフラグがオン」という。

 TCPは,これらの6種類のフラグをオンにするかオフにするかで,通信相手にさまざまな情報を伝えるのである。

ミニ解説2
PSHやURGフラグのナゾ
 TCPヘッダーの制御ビットのうち,PSHフラグとURGフラグは,今ではちょっとナゾの存在になっている。もともとのRFCでは,PSHフラグは「TCPセグメントを受け取ったら速やかに上位プロトコルへ渡す」,URGフラグは「緊急データ送信のために使う」と規定されている。ところが,TCP/IPのプログラムを開発しているアクセスなどによると「実際にはフラグのチェックはするものの,特別な処理はなにもしていない」(筬島雅之研究員)と言う。守るべきRFCと世の中の実装が必ずしも一致していない一例である。

三つの手順であいさつを交わす

 では,実際にこのフラグをTCPがどのように使っているかを見ていこう。まずは通信相手と接続するときと,切断するときの手順からだ。

 TCPでは,相手との通信に先立ち,必ず仮想的な通信路を作る。こうすることで,ほかの通信と完全に分離され,アプリケーションはあたかも相手と1対1で通信しているように使える。この通信路のことを「TCPコネクション」と呼び,相手とTCPコネクションを作ることを「TCPコネクションを確立する」,「TCPコネクションを張る」などと表現する。

 TCPコネクションを確立するときは,「3WAY(スリーウェイ)ハンドシェーク」と呼ぶ特別な手順を実行する(図4)。それは,通信相手BにSYNフラグをオンにしたTCPセグメントを送るところから始まる(1)。これは,AからBの方向へデータを送れる通信路を確保したいという通信許可の要請である。これを受け取ったコンピュータBは,通信を許可するかどうかを判断し,OKなら返事としてACKフラグとSYNフラグがオンのTCPセグメントをAへ返信する(2)。これは,AからBへの通信を許可するとともに,BからAへの通信許可を求めるという意味だ。そして最後に,コンピュータAがACKフラグをオンにしたTCPセグメントをBへ送り,コネクションが確立する(3)。

図4●TCPコネクションの確立と切断手順
 
[画像のクリックで拡大表示]

 このように3回(3WAY)のやりとりが行われるので3WAYハンドシェークと呼ぶわけだ。ちなみにハンドシェークとは「握手」という意味である。

 ここのもう一つのポイントは,一つのTCPコネクションで自分から相手方向への通信路と,相手から自分への通信路の両方,すなわち全2重通信が可能な通信路が作られるという点だ。

 なお,TCPコネクションが確立しているかどうかは,UNIXやWindowsに付属するNETSTAT(ネットスタット)コマンドで簡単に確認できる(図5)。

図5●TCPの接続状態はWindowsに付属するNETSTATコマンドで確認できる
 
[画像のクリックで拡大表示]

切断時も礼儀正しくあいさつ

 次にTCPコネクションの切断手順を見てみよう(図4参照)。接続時と同様に,TCPは切断するときも礼儀正しくあいさつを交わす。

 具体的には,切断したい側のコンピュータAが,まずFINフラグをオンにしたTCPセグメントを相手Bに送る(1´)。これを受信したコンピュータBは,ACKフラグをオンにしたTCPセグメントを送り返す(2´)。これでAからB方向へのコネクションが切断された状態になる。

 この直後に,コンピュータBはFINフラグをオンにしたTCPセグメントもAへ送る。これはBからA方向へのコネクションを切断する要求だ。そして,この切断要求を受信したコンピュータAがACKフラグをオンにしたTCPセグメントを返すと(3´),TCPコネクションは完全に切断される。

 なお,コネクション確立時と違って,切断手順はいくつかのパターンがある。例えば,RSTフラグを使うと,問答無用で切ることができる。

ミニ解説3
シーケンス番号を使い切る可能性は?
 シーケンス番号は,32ビット分の大きさなので,232すなわち0~42億9496万7296までの値をとることができる。つまり,約4Gバイトのデータを送るまではシーケンス番号を使い切ることはない。しかし,最近のパソコンではこれくらい大きなファイルを扱うことも珍しくない。もしシーケンス番号が上限まで達したらどうなるか。実は「何も起こらない」。0に戻るだけである。そもそもシーケンス番号の初期値は0からではなく,時刻情報などを基にしてランダムに決められる。したがって,すぐに0に戻ってしまうこともある。なお,実際に4Gバイト以上のデータを送信して,シーケンス番号がひとまわりしても,まず問題は起こらない。

データ転送はACKのキャッチボール

 役割1の最後は,TCPコネクションを確立したあとのデータ転送の基本的なやりとりを見ておこう(図6)。

図6●TCPによるデータの転送の基本的な流れ
シーケンス番号と確認応答番号を使って,データが相手に届いたことを確認しながらデータをやりとりする。
[画像のクリックで拡大表示]

 データ転送手順は,通信相手からなんらかのTCPセグメントを受け取ったら,その証(あかし)としてACKフラグをオンにしたTCPセグメントを返信するだけである。

 ただ,Webブラウザなどのアプリケーションから渡されたデータの大きさは一定ではない。非常に大きい場合もある。こうした場合は,長い巻物を適当な長さで切って,その切れ端を別々に送るイメージになる。しかし,各切れ端は,元の長い巻物のどこに相当するのかという情報を含んでいない。

 したがって,TCPは送信する切れ端(データ)が巻物のどの部分に該当するのかを別の手段で通信相手に伝える必要がある。そのために使うのがヘッダー中の「シーケンス番号」である。 図6のように,データを送り始めたときのシーケンス番号が1001番だったとしよう。すると,TCPは新たに500バイトのデータを送るときに,シーケンス番号として1001を設定して,500バイトのデータを入れて送る(1)。

 一方,データを受信した側(図ではB)は,巻物のどこまでを受け取ったかを送信側に教えるために,ヘッダー中の確認応答番号の領域に,1501と入れてACKフラグをオンにしたTCPセグメントを返信する(2)。これは,「1001番から始まったデータの500バイト分を受け取ったので,次は1501番から下さい」という意味である。そして,Aが続きの1000バイトを送信するときは,シーケンス番号に1501を入れて送信する(3)。

役割2 通信制御
最適な速度で確実にデータを届ける

 TCPは通信の信頼性を高めるためにさまざまな工夫を凝らしている。それは大ざっぱに分けて,(1)ウインドウ制御,(2)再送制御,(3)ふくそう制御--の3種類である。これらのしくみこそ,TCPのキモと言える部分だ。では,順番に詳しく探っていこう。

一歩ずつでは時間の半分がムダ

 役割1では,送信側がTCPセグメントを一つ送ると,受信側はそれに対する確認応答(ACK)を一つ返すという基本的なデータのやりとりを紹介した。しかし,この方法ではこちらから相手にTCPセグメントを送って,相手から確認応答が返ってくるまで次のデータを送れない。

 一方,TCPを使った通信は,Telnetのようにコマンドなどの小さいデータをゆっくりとやりとりするケースから,FTPのように大きなデータを一挙に送るケースまでさまざまである。役割1で紹介した方法でも,Telnetのようなアプリケーションならそれほど大きな問題は生じない。しかし,FTPでファイルをダウンロードするような場合には,ムダが多すぎる。

複数のセグメントをまとめて送る

 そこで,TCPは「ウインドウ制御」という方法を使って,信頼性を確保したままデータ転送のスループットを高める工夫をしている。

 ウインドウ制御を一言でいうと,「相手からの確認応答を待たずにTCPセグメントを続けて送れるようにする方法」である。そのために,「ウインドウ」と呼ぶシカケが用意される。  ウインドウとは,まとめてデータを受け取れるように受信側のコンピュータが用意しておくバッファ・メモリーのことだ。このウインドウの大きさを受信側が送信側に通知することで,受信側はデータを取りこぼすことなく受信できる。

 ウインドウの大きさのことは「ウインドウ・サイズ」と呼ばれ,TCPヘッダー中のウインドウ・フィールドにその大きさが書き込まれる。このフィールドの大きさは16ビットなので,ウインドウ・サイズは0~65535までの値,すなわち最小0バイトから最大64Kバイトまでの任意のサイズになる。

接続時に受信の限界量を通知し合う

 実際にウインドウ制御のしくみを使ったデータ転送のやりとりを見てみよう(図7)。

図7●実際のデータ転送は“ウインドウ”を単位として行う
図7●実際のデータ転送は“ウインドウ”を単位として行う
相手に通知するウインドウ・サイズを増減させることでデータの送信量を制御(フロー・コントロール)できる。

 TCPを使って通信しようとする2台のコンピュータは,まずTCPコネクションを確立する。ここは,役割1で見てきた通りである。実は役割1ではあえて省略したが,このコネクション確立時には,お互いのウインドウ・サイズも通知し合っている。したがって,コネクションを確立した時点で,お互いのウインドウ・サイズは知っている。

 そして,実際のデータ転送が始まる。このとき,送信側は,相手から確認応答が届かなくても気にせずに,相手のウインドウ・サイズを超えない範囲でデータを続けて送信する。例えば,パソコンBの初期ウインドウ・サイズが3000バイトだとすると,パソコンAは1000バイトのTCPセグメントを三つまで続けて送信できるというわけだ。

データ流量は受信側がコントロール

 この方法でデータを送っていると,やがて送信側は受信側のウインドウ・サイズいっぱいまでデータを送信してしまう可能性がある。この場合は相手から確認応答が返って来るまで待たなくてはならない。確認応答が返ってくると,そのTCPセグメントのヘッダー部分には,新たにデータを受信できるウインドウ・サイズが記述されているはずなので,送信側はまたそのウインドウ・サイズまで続けてデータを送信できるようになる。

 一方,受信側ではTCPセグメントを受け取って,そのデータを処理し終えると,受信バッファ(ウインドウ)がその分空くことになる。そこで受信側は確認応答のときに,このウインドウ・サイズを伝えられる。そうすれば,送信側は新たにその分だけデータを送信できるようになる。

 このウインドウ制御のポイントは,ウインドウの値を受信側が変化させることでデータ受信量を制御できるという点だ。もしバッファがいっぱいになって,それ以上データを受信できない場合は,ウインドウ・サイズを「0」にした確認応答を返すことで,送信側とコネクションを張ったまま一時的にデータ転送を止めることができる。

 この一連のウインドウ制御の流れを別の視点から見ると,送信側が用意するデータ全体の一部に窓(ウインドウ)を開けたイメージになる(図8)。そして,受信側はこの窓を徐々にずらしながら,すべてのデータを受信していく。この様子から「スライディング・ウインドウ方式」ともいう。

図8●ウインドウ制御を別の視点から見てみる
図8●ウインドウ制御を別の視点から見てみる
送信側のAが用意するデータ全体の一部に窓(ウインドウ)を開け,そのウインドウを少しずつずらしていくイメージだ。

データの並べ替えも大切な仕事

 ただ,ウインドウ制御を使うときには,役割1で紹介したやりとりには要らなかった別のしくみが必要になる。その一つは,受信側で受信データを正しい順番に並べ替えることだ。

 送信側では,大きなデータをTCPセグメントに入るデータに切り分けて順番に送信する。しかし,このTCPセグメントが受信側でも順番通り到着するとは限らない。そこで,受信側では受信バッファ内でシーケンス番号を基に受信データを並べ替える(図9)。

図9●ウインドウ制御のために,送信側もバッファ・メモリーを用意する
ウインドウ・バッファはTCPコネクションごとに用意される。
[画像のクリックで拡大表示]

 また,送信側も受信側と同じだけの送信バッファを持っていなければならない。送信したTCPセグメントの一部だけが相手に届かなかったりする可能性があるためだ。このあとで詳しく説明するが,TCPセグメントの一部が受信側に届かないと送信側が再度データを送らなければならない。このためには,一度送信したデータもバッファに貯めていなければならないのである。

ミニ解説4
もっと賢い再送制御
 TCPによる再送制御の基本は,本編で見たように,TCPセグメントの往復時間に基づいた時間から決める。しかし,最近では送信効率を高めるために「高速再転送」(fast retransmission)という新手法が提案されており,Windowsなどの多くのOSが採用している。この高速再転送では,相手からまったく同じ内容の確認応答が3回(Windowsは2回)返ってきたら,TCPセグメントを紛失したと判断して再送する。

データの再送は時間で判断

 信頼性の高い通信を実現するためには,TCPセグメントが途中で紛失しても,それを検出して再送する手段が必要になる。次はこのTCPによる再送制御のしくみを見ていこう(図10)。

図10●途中で紛失したTCPセグメントは再送する
図10●途中で紛失したTCPセグメントは再送する
いずれのパターンもAはしばらく待ってから再送する。再送が必要なケースは大きく3パターンある。

 TCPセグメントが紛失するケースとしては,(1)送信したTCPセグメントが途中でなくなる,(2)受信側からの確認応答が途中でなくなる――という二つの可能性が考えられる。また,TCPセグメントを実際には紛失しなくても,途中のネットワークが混雑して確認応答が送信側まで届くのに長い時間がかかるケースもある。TCPはこれらのどのケースでも,TCPセグメントを途中で紛失したと考えて再送処理を行う。

 この再送制御を行うのは,送信側が主体になる。送信側がTCPセグメントを送信したら,これに対応する確認応答が返ってくるはず。そこで,一定時間内に確認応答が返ってこなかったら,送信側は途中でTCPセグメントを紛失したと判断する。そして再度TCPセグメントを送信するのである。

 この再送を行うかどうかを判断する時間は,これまでに送ったTCPセグメントに対する確認応答が返ってくるまでの時間(RTT)から算出する。TCPは測ったRTTを見て,RTT値が大きければ,再送すると判断するまでの時間を長くし,小さければ短くする。RTTはネットワークの混雑状況や相手との距離によって違ってくるので,再送するかどうかを判断する時間も,こうしたネットワーク環境を反映することになる。

ふくそう制御は送信側が主導

 TCPには「ふくそう」の制御もある。ふくそうとは,コンピュータが回線の伝送能力以上にデータを送ろうとしたり,途中にあるルーターの処理能力が追いつかなかったりして,送受信データが効率よく送れなくなったり,途中でひんぱんに破棄されてしまう状態のことだ。最悪の場合,データを送ろうとしては破棄され,それを再送しようとして再びふくそうを起こすという悪循環に陥ってしまう。信頼性の高い通信の提供を目的とするTCPにとって,ふくそうの発生はなんとしても避けたい事態なのである。

 そこでTCPでは,2段構えでふくそうを回避するためのしくみ(アルゴリズム)を用意している。それが「スロー・スタート・アルゴリズム」と「ふくそう回避アルゴリズム」である。

 まずデータ転送が始まると,最初にスロー・スタート・アルゴリズムによるふくそう制御がかかる。これでは,最大セグメント・サイズ(MSS)一つ分までのデータを送り,これに対する確認応答が返ってきたら,次は確認応答が返ってきた数だけ増やして送信するという具合にMSSの整数倍の単位でデータ転送量を増やす(図11)。

図11●TCPによるふくそう制御のしくみ
送信側は本来のウインドウ・サイズとは別に「ふくそうウインドウ」と呼ぶ値をもち,通信状況に応じてこれを変化させる。具体的にどのようにふくそうウインドウを変化させるかは実装によって異なる。
[画像のクリックで拡大表示]

 こうして送信可能なセグメント数が一定のしきい値を超えたら,今度はふくそう回避アルゴリズムを使う。この段階では,スロー・スタートと違い,送信可能なデータ量を1MSS分ずつゆるやかに増やしていく。

 このように2段構えにして,ふくそうが起こる可能性が低い最初の段階は送信データ量を素早く増やし,ふくそうが起こる可能性が高まってきたら送信データ量を少しずつ増やしていく。

 ただ,送信データ量は徐々に増えていくので,いずれはふくそう状態に突入してしまうかもしれない。TCPはTCPセグメントが紛失したら,ふくそうが起こったと判断することにしており,ふくそうが起こるとデータ送信量をいったん減らす。そして,スロー・スタートあるいはふくそう回避の手順で,再び送信データ量を徐々に増やしていく。

FTPダウンロードが遅い理由とは

 ここまでTCPがいかにうまく通信を制御して,データ伝送のスループットを向上させているかを見てきた。しかし,実はそう誉めてばかりもいられない。TCPを使うがゆえに発生する限界もある。しかも,それはごく身近なところで起こる。なかには実際に体験しているユーザーもいるはずだ。

 その一つが,ウインドウ・サイズによってスループットが頭打ちになるという問題である(図12)。スループットとは,1秒間にどれだけのデータ量を伝送できるかという値。TCPの場合,このスループット値は,ウインドウ・サイズを確認応答が返ってくるまでの往復時間(RTT)で割ることで求められる。

図12●回線が速くてもFTPなどのダウンロードが遅くなる原因の一つはTCPにある
インターネットなど遅延が大きいネットワークではスループットはウインドウ・サイズの制限で頭打ちになってしまうためだ。
[画像のクリックで拡大表示]

 例えば,往復時間(RTT)が0.5秒かかるネットワークの場合,スループットは理論上128Kバイト/秒(約1Mビット/秒)を超えることはできない。RTTが0.5秒というのは,インターネットでは特別珍しいことではない。ルーターを何台も経由した遠隔地にあるFTPサーバーからファイルをダウンロードするようなケースでは,こうした状況に陥るのである。

 つまり,TCPを使うFTPなどでは,回線が8MのADSLサービスだろうが100MのFTTHサービスだろうが,どんなに高速なサービスを使っても,そしてサーバーまでの回線速度がどんなに速くて混雑していなくても,ダウンロード速度は回線速度に関係なく落ち込んでしまうのだ。

 元々スループットを高めるために導入されたウインドウ制御のしくみは,現在のブロードバンド・サービスなどを利用する際にスループットが出ない“足かせ”になってしまっているのである。

ウインドウは大きすぎても困る

 ではTCPを使う限りこの問題は解決できないかというとそうでもない。実は,TCPのオプションとして「ウインドウ・スケール・オプション」というしくみが用意されている。このオプションを使うとウインドウ・サイズを64Kバイトの2倍,4倍,8倍と,最大1Gバイトまで拡張できる。

 すでにWindowsなどは,このウインドウ・スケール・オプションをサポートしているので,すぐに使える下地はある。しかし,実際にこのオプションを使っている例はほとんどない。

 ウインドウ・サイズを増やすということは,データを受信する側がTCPコネクション1本ごとにそれだけの大きなメモリーを確保する必要があるからだ。最近のパソコンがいくら高性能だからといっても,TCPコネクションごとにそんなにメモリーは使えない。また,当然このオプションを使うためには,相手も対応していなければならない。つまり,相手も同じように送信バッファを用意しなければならないのだ。

 TCPコネクションを同時に複数確立したり,UDPを使うことでウインドウ・サイズの問題を回避した方が,現実的かもしれない。

ミニ解説5
TCPを使う限り,1Gbpsは出ない?
 TCPでは,最大64Kバイトというウインドウ・サイズを使っている限り,どんなに回線速度が速くてもインターネット環境ではスループットが頭打ちになってしまう。例えば,現在のFTTHを超える1Gビット/秒という超高速のブロードバンド・サービスが登場しても,TCPを使って実際に1Gビット/秒のスループットを実現するためには,往復時間(RTT)が0.000065535秒以下である必要がある。この時間というのは,光(真空中で1秒間に約30万km)ですら20km弱しか進めない。したがって,インターネット環境では,TCPコネクション1本で1Gビット/秒のスループットを出すのは事実上不可能なのだ。唯一の解決策は,ウインドウ・サイズを大きくするウインドウ・スケール・オプションを使うことだ。

役割3 アプリケーションとの仲介
適切なアプリケーションをポート番号で見分ける

 TCPにはもう一つ大事な役割がある。受信データを適切なアプリケーションへ受け渡す「仲介役」である。役割1役割2で見てきた「あいさつ」やさまざまな制御によって,目的のコンピュータにデータが届くと,TCPはそれを最終的な受信者であるアプリケーションに渡すのだ。そのために重要な役割を果たすのが「ポート番号」と「ソケット」という二つのキーワードである。

ミニ解説6
TCPのオプションと実装の関係
 TCPは最初の仕様をずっと守りながらも,時代とともに次々と新しい仕様が提案され,オプションの形で取り入れられている。オプションの種類は10種類以上あるが,ほとんどのOSが採用しているのがMSSオプションである。これはTCPコネクション確立時に相手に最大セグメント・サイズを通知するために使う。このほか,Windowsでは,ウインドウ・サイズを拡張するウインドウ・スケール・オプションや,途中で抜け落ちたTCPセグメントだけを指定して再送要求する選択確認応答(SACK:selective ACK)オプション,TCPセグメントに時刻情報を埋め込むタイムスタンプ・オプションなどが利用できる。

ポート番号で識別

 TCPは,受け取ったデータをどのアプリケーションへ渡せばいいかを判断するために,TCPヘッダー中に書かれた「ポート番号」と呼ぶパラメータを使う。役割1で紹介したプロトコル・フォーマットの図(図3)のように,TCPヘッダーにはあて先と送信元のポート番号が書かれている。TCPはこのあて先ポート番号を見て,受信データをどのアプリケーションに渡すかを判断するのである。

 例えば,あて先ポート番号が「80番」ならWebサーバー,25番だったら電子メールの送信に使うSMTPサーバー,23番ならTelnetサーバーという具合だ(図13)。したがって,それぞれのアプリケーションが使うポート番号は,ほかのアプリケーションと重複していてはならない。

図13●ポート番号はTCPが受け取ったデータをアプリケーションに渡すための「窓口番号」の役割を果たす
 
[画像のクリックで拡大表示]

アプリケーションが番号を宣言

 ここで誤解しやすいのは,アプリケーションやサーバー・ソフトとポート番号の関係である。ポート番号とアプリケーションの関係はあらかじめ決まっているわけではなく,アプリケーションが起動時などに自分で使うポート番号をTCPに登録するのである。

 したがって,80番ポートあてに届いたデータでも,80番ポートにWebサーバー・ソフトが登録されていなければ,TCPはWebサーバー・ソフトにこのデータを渡さない。80番ポートを利用するアプリケーションがない場合は,「到達不可能」というメッセージをクライアントへ返すだけである。逆にWebサーバー・ソフトを8080番ポートに割り当てて稼働させれば,TCPは8080番ポートあてに届いたデータをWebサーバー・ソフトに渡す。

 つまり,そのコンピュータあてに80番ポートを指定したTCPセグメントが届いたとき,そのセグメントのデータをWebサーバー・ソフトがほしいなら,Webサーバー・ソフトはTCPに対してあらかじめ80番ポートを使いたいと宣言しておかなければならないのである。

 ただ,Webサーバーのような重要なサーバー・ソフトは,世界中で共通のポート番号を使わないと大きな混乱が生じてしまう。Webブラウザなどのクライアント・ソフトが,決まったポート番号(通常は80番)を想定してTCPコネクションをつなぎに行くからだ。そこで,TCP/IPの世界では,0~65535番までのポート番号のうち,1023番までをサーバー・ソフト専用の予約領域として,サーバー・ソフトの種類別に割り当てている。

仲介役の実体は「ソケット」

 もう少し,このポート番号の扱いを掘り下げていこう。

 そこには,「ソケット」と呼ぶしくみが介在する(図14)。ソケットは,通信機能(TCP/IPソフト)とアプリケーションを結びつけるためのインタフェースの役割を果たす。アプリケーションがTCPで通信したいとき,まずソケットを作成し,このソケットを介してTCPコネクションを確立して,データを送受信する。個々のソケットはIPアドレスとポート番号の組み合わせで区別される。TCPとアプリケーションの間に仮想的なパイプを設けるイメージである。

図14●アプリケーションは「ソケット」を介して通信する
 
[画像のクリックで拡大表示]

 もう少し具体的に見てみよう。アプリケーションは,TCPで相手と通信したいとき,まずsocket(ソケット)というプログラムを呼び出してソケットを生成する。そして,このソケットに対して,サーバー・ソフトならbind(バインド)という命令を使って自分が待ち受け用として使いたいポート番号を指定する。Webサーバー・ソフトなら,ここで80番ポートを指定することになる。

 一方,クライアント・ソフトはconnect(コネクト)という命令を呼び出し,通信相手のIPアドレスとポート番号を指定して,TCPコネクションを確立させる。すると,ソケットがTCP/IPソフトに指定された通信相手とのTCPコネクションを確立するように要求し,実際に確立される。TCPコネクションが確立したあとは,アプリケーションはソケットに対してデータを送れば,通信相手にデータが届き,ソケットから受信データを受けとる。

ブラウザが複数同時に動く理由

 1台のサーバー・マシンで,WebサーバーやSMTPサーバーなど,複数のサーバー・ソフトが動いていてもTCPはポート番号によってそれらを区別する。それと似ているが微妙に違う現象がある。それは,Webブラウザなどを複数起動したとき,なぜそれぞれのブラウザが間違えることなくデータを受信して表示できるのかという点である。

 先ほどのサーバー接続時のポート番号の考え方からすると,TCPはWebサーバーから返信されてきたTCPセグメントのポート番号を見て,あて先がWebブラウザだと判断し,ブラウザにデータを渡すことになる。しかし,Webブラウザが複数存在する――これにはどんなカラクリがあるのだろうか。

 実は,この答えは簡単だ。Webブラウザを複数起動すると,それぞれのブラウザのウインドウは別々にTCPコネクションを張り,TCPからそれぞれ異なるポート番号が割り当てられるのである(図15)。

図15●1台のコンピュータ上でWebブラウザを複数起動してもそれぞれで正しく通信できるカラクリ
 
[画像のクリックで拡大表示]

 ここでのポイントは,クライアントのポート番号は任意の番号で構わないという点である。すでに見てきた通り,TCPで通信する以上,サーバーだろうがクライアントだろうが,自分が使うユニークなポート番号が必要である。そして,TCPコネクションを確立する際には,それぞれ相手のポート番号をあて先として指定する。

 ただ,サーバー・ソフトは一般に相手からの接続を待ち受けるものであり,逆にクライアント・ソフトは自分からサーバーに接続するものである。したがって,TCPコネクションを確立する段階では,サーバー側のポート番号さえわかっていればよい。クライアントは,どんなポート番号を使っていても,TCPコネクションを確立する段階で送信元ポート番号として相手(Webサーバー)に知らせれば,サーバーはそこから返信相手がわかる。

 こうしたクライアントのポート番号は,自分で指定することもできるが,TCPに任せるのが一般的である。この場合,TCPは1024番以上のポート番号のうち,その時点でほかのアプリケーションが利用していない番号を適当に割り振る

 つまり,図15のように,一つ目のWebブラウザには2000番,二つ目には3000番,三つ目には4000番という具合にWebブラウザが起動してTCPコネクションを確立するたびに別々のポート番号をTCPが割り当てているのだ。そして,TCPはWebサーバーからのTCPセグメントを受けとると,そのあて先ポート番号を見て,どのWebブラウザに渡すべきかを判断して,適切なWebブラウザに受信データを渡すのである。

番号が足りなくなることはない?

 TCPは,クライアント・ソフトにポート番号を割り振るだけではなく,不要になったポート番号をほかのクライアント・ソフトが再利用できるようにするしくみも持っている。

 まず,Webブラウザを一つ起動すると,TCPコネクションが確立され,TCPによって任意のポート番号が割り当てられる。このTCPコネクションは,Webブラウザがページ・データを受信し終わって少し経過すると切断される。そして,またほかのWebページを開こうとすると,新たにTCPコネクションを確立する。この別のTCPコネクションには別のポート番号が割り当てられる。

 したがって,このままだとクライアント・ソフトはどんどんポート番号を食いつぶしていく。ポート番号は有限(2の16乗個=6万5536個)なので,最後には割り当てられるポート番号がなくなってしまう。

 そこで,クライアントが使ったポート番号は,一定時間経つとTCPが再利用できるようにする。具体的には,コネクションが切断されると,そのコネクションは「TIME WAIT」という状態になり,しばらくの間,そのポート番号をだれも利用できない状態にする。そして,その後一定時間経つとTIME WAIT状態が終わり,TCPはポートを開放してほかに割り当てられるようにする(図16)。

図16●ポート番号を再利用するしくみ
アプリケーション側で特に指定しなくても,使い終わったら,ほかで使えるようになる。
[画像のクリックで拡大表示]

 このポートを開放するまでの時間は,TCPの仕様を決めているRFCによると「2MSL 時間よりも大きくする」と決まっている。1MSLというのは,TCPセグメントの到着がどんなに遅れたとしても,最大これくらいの時間以上経ってから届くことはないと考えられる時間で,通常約2分程度に設定されている。したがって,2MSL時間というのは,相手からの確認応答やデータが遅れて届く最大の時間(約4分)ということになる。

 ただ,現実には4分という時間は少々長すぎる。そこでTCPの実装によってはMSLを30秒程度と短く設定しているケースもある。

 ともあれ,このようにしてTCPは有限のポート番号が枯渇しない程度に再利用しながら,アプリケーションに割り当てているのである。


Part3 UDP編–面倒な手順をカット,信頼性より軽さと速さ

2007/03/14
斉藤 栄太郎=日経NETWORK

Part2では,TCPが通信の信頼性を高めるためのしくみを見てきた。一方,UDPはこれとは正反対で,信頼性を確保するしくみを持っていない。その反面,とても自由度が高いプロトコルである。Part3では,UDPの概要とともに,どんなアプリケーションに向いているのかを見ていく。

 TCP編では,TCPが通信の信頼性を高めるためのしくみを見てきた。一方,UDPはこれとは正反対で,信頼性を確保するしくみを持っていない。だからといって,UDPは不要と決めつけてしまうのは早計である。どんなプロトコルにも長所と短所があり,それぞれにふさわしい用途がある。

“なにもしない”プロトコル

 まずはUDPとはどんな姿をしているのか,UDPのプロトコル・フォーマットから見ていこう(図1)。

図1●UDPのプロトコル・フォーマット
UDPのヘッダーはIPパケットにポート番号を追加しただけで,特別なことは何もしない。
[画像のクリックで拡大表示]

 UDPは,TCPと同じくIPの上位プロトコルである。TCPのデータはセグメントと呼んだが,UDPではそれをデータグラムという。実際には,このUDPデータグラムがIPパケットのデータ部分に入ってやりとりされる。

 図1を見ればすぐわかるように,UDPのヘッダー部分は非常にシンプルだ。(1)送信元ポート番号,(2)あて先ポート番号,(3)UDPデータグラムの大きさ,(4)チェックサム――という4種類のフィールドしかない。TCPヘッダーにはあったシーケンス番号やウインドウ値,制御ビットといった項目が削られている。残った4項目は,UDPがアプリケーションとの間でデータを受け渡すために最低限必要なものだけである。要するに,UDPはポート番号でアプリケーションを区別してデータを受け渡す以外,「なにもしない」プロトコルなのである。

 しかし,なにもしないのは悪いことばかりではない。なにもしなければ,それだけ処理が軽くなり,TCPより高速に処理できる。例えば,UDPのヘッダー・サイズはたったの8バイトである。TCPヘッダーが標準で20バイト,オプションを使うと最大60バイトになるのに対して圧倒的に小さい。余計なヘッダーが付かない分だけアプリケーション・データを送受信できるので,通信効率はUDPの方が高くなる。

 また,信頼性を確保するしくみをアプリケーションが独自に用意したい場合もあるだろう。再送処理だって,TCPに任せずにアプリケーションが独自に処理してもよい。つまり,UDPはとても自由度が高いプロトコルなのだ。

同報通信ができるのはUDPだけ

 では,具体的にUDPがどんなアプリケーションに向いているかというと,大きく三つ考えられる(図2)。

図2●UDPの用途は三つある
とくに同報通信はUDPしか使えない。
[画像のクリックで拡大表示]

 まず一つ目は,複数の相手へメッセージを一斉同報するアプリケーションだ。これは向いているというよりも,TCPにはできない絶対的な利点である。TCPは1対1でTCPコネクションを張るのが前提だからだ。

 メッセージの同報は,とくにLANでTCP/IPを使うときに必要になる。例えばIPアドレスをクライアント・マシンに自動で割り振るDHCPが,その代表である。そもそもクライアント・マシンはIPアドレスを持っていないから,TCPではまったく役に立たない。また,ネットワーク機器を管理するSNMPや,ルーター同士で経路情報を交換するためのRIPといったプロトコルも同様にUDPを使う。

 二つ目は,ストリーミング・データを扱うアプリケーションだ。インターネット電話などでは,高速でリアルタイムにデータを送受信する必要がある。もし途中のデータが欠落しても,あとから再送してもらっていたのでは間に合わない。つまり,多少のデータ欠落は我慢してでもUDPを使った方がいいのだ。

 三つ目はドメイン名からIPアドレスを調べたりするDNSである。DNSでは,1回の問い合わせに必要なデータ量がIPパケット1個分で済んでしまうくらい小さい。TCPコネクションを確立するために3パケットをやりとりすること自体がムダなのである

 結局,TCPとUDPはどちらも絶対必要なのである。

ミニ解説7
同じポート番号が同時に使える!?
 Webサーバーが80番ポートで動いているのに,DNSサーバーも80番ポートで動く――にわかには信じられないようなことが,実際には起こり得る。TCPとUDPはそれぞれで同じ0~65535番までのポート番号を使うが,この管理が完全に独立している。だから,こんな不可解な現象が起こり得るのだ。ただし,実際にはHTTPはTCPの80番ポートとUDPの80番ポート,DNSもUDPの53番ポートとTCPの53番ポートという具合に両方を予約していることが多く,冒頭のようなことは起こりにくい。
カテゴリー: blog, ソフト・ハードウェア パーマリンク

コメントを残す

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