LAN技術研究室(LANに関心がある人のために情報を提供しているサイト)
このページでは、TCP(Transmission Control Protocol)の基礎について初心者向けに解説しています。
LANの初歩
トランスポート層のプロトコル
TCP(Transmission Control Protocol)
TCP(Transmission Control Protocol)は、OSI参照モデルにおけるレイヤ4のトランスポート層のプロトコルです。
TCPは、一つ下の階層のレイヤ3(ネットワーク層)のIPと一つ上の階層のレイヤ5のセション層との間に入って情報の中継の役割を果たします。
TCPでは、アプリケーションプログラムからデータを貰うと、TCPセグメントと呼ばれるパケットのようなデータの塊に分割します。
TCPセグメントは、IPで言えばIPパケットのようなものです。
このTCPセグメントにTCPヘッダー(表1)というタグやラベルのようなものを頭に付けて、下位のレイヤに相当するIPに渡して送信されます。
受信した後は、下位のレイヤ(IP)からIPヘッダーをカットされたTCPセグメントが渡されてきます。
TCPでは、このTCPセグメントに記述されている宛先ポート番号を見て適切なアプリケーションプログラムにデータを渡す役割を果たします。
おおざっぱに言うと、TCPというプロトコルはだいたいこんなことをしているのですが、実際はもっとずっと複雑なことをしています。
前述のように、電子メールソフトやメールサーバープログラムのようなアプリケーションプログラムを開発するソフトウェア技術者にとっては、ソケットインターフェイスと呼ばれるAPI(Application Programming Interface)を使用してプログラムを実行してしまうため、TCPに関してはあまり意識しませんが、知識として身に付けておいた方が良いです。
また、組み込み機器においては、TCPとその下位レイヤのプロトコルも含めた仕組み自体を実装することがあります。
つまり、プロトコルスタックを開発し、組み込み機器に実装する場合は、必須の知識です。
しかし、特にTCPの部分が大変な開発になり、そう簡単には開発できません。
そこで、商用のTCP/IPプロトコルスタックもあり、こういったものを利用することもできます。
TCP/IPを利用して通信を行うには、これらの抽象的なプロトコルという概念をソフトウェアの集合体で具体的に実現する必要があります。
そこで、このようなソフトウェアの集合体は、OSI参照モデルなどのプロトコル階層構造に従って、下から積み上げたような構造を成しています。
実際に、西洋の皿を積み上げる器具(スタック)のように、下からこれらのソフトウェアを積み上げていきます。
このようなソフトウェアの構造をプロトコルスタックと呼びます。
プロトコルスタックは、OSや通信の目的によっても内容が変わります。
つまり、貨物列車の荷物の入れ替えのように積み上げるプロトコルに対応するソフトウェアを入れ替える必要があります。
さて、TCPの概要はこのくらいにして、TCPヘッダーの説明、TCPにおける伝送制御手順、TCPの処理詳細の順に詳しく説明していきます。
- TCPの役割と特徴
-
TCPは、どのような役割をするプロトコルなのでしょうか。
簡単にまとめてみますが、詳しくは以下のTCPヘッダーの説明以降で解説します。
- 信頼性が高いコネクション型の通信
- スリーウェイハンドシェイク(3 way handshake)
- 順序制御
- 誤り訂正と再送
- フロー制御と輻輳制御
- 単一ホストでも複数のポート番号を使用することで仮想通信路を複数のアプリケーションに提供する。
TCPにおける仮想通信路をコネクションと呼びます。
下位プロトコルのIPにはない機能です。
スリーウェイハンドシェイク、順序制御、誤り訂正と再送、及び、フロー制御と輻輳制御によって、信頼性の確保を行っています。
信頼性が高いコネクション型の通信である反面、相手ホストへの接続に時間がかかったり、同じトランスポート層のプロトコルであるUDP(User Datagram Protocol)より通信速度が遅いです。
従って、信頼性より高速性を要求される通信には向きません。
- TCPヘッダーとその説明
-
|
送信元ポート番号
(16 bit)
|
宛先ポート番号
(16 bit)
|
|
シーケンス番号
(32 bit)
|
確認応答番号
(32 bit)
|
ヘッダー長
(4 bit)
| 予約
(6 bit)
|
フラグ
(6 bit)
|
U
R
G
| A
C
K
| P
S
H
| R
S
T
| S
Y
N
| F
I
N
|
|
|
| ウィンドーサイズ
(16 bit)
|
|
チェックサム
(16 bit)
|
緊急ポインタ
(16 bit)
|
|
TCPオプションとパディング
(32 bit)
|
表1
上記の表1に書いたTCPヘッダーの説明をします。
- 送信元ポート番号
-
16ビットの長さなでの、0〜65535までのポート番号を設定し、この番号は送信元を表しています。
ただし、0のポート番号は予約されています。
また、ポート番号は、皆さんが良く知っているアプリケーションプログラムなどで予約されている番号がたくさんあります。
独自のネットワークアプリケーションプログラムを開発する際、これらの番号は避けなければなりません。
つまり、1023番以下は勝手に使えないということです。
例えば、Webサーバーなら80番、電子メールの配達に必要なSMTPサーバーは25番を使っています。
- 宛先ポート番号
-
0〜65535までのポート番号を設定し、この番号は宛先を表しています。
- シーケンス番号
-
ネットワークにどんどん出て行くTCPセグメントにシーケンス番号という番号を付与します。
一連のTCPセグメントの流れをストリームと言いまして、この流れの中で1つのTCPセグメントがどの位置に相当するかがわかるように番号が付与されます。
シーケンス番号は送信側で処理されます。
シーケンス番号は、確実に相手にパケットを届けるためには必要となります。
- 確認応答番号
-
確認応答番号は、どの位置まで受信したかを表す数値で、位置はオクテット単位で示されます。
確認応答番号を見ると、次に受信するTCPセグメントのシーケンス番号がわかります。
- ヘッダー長
-
TCPヘッダーの長さが設定されます。
ただし、4ビットしかないフィールドなので、長さがオクテット単位でそのまま設定できるわけがなく、0〜15までの値しか設定できません。
そこで、32ビット(4オクテット)単位で設定されます。
これだと、15×4=60オクテットまでTCPヘッダーの長さを設定できるため、一番最後のTCPオプションがどんなに長くなっても60オクテットということになります。
- 予約
-
予約されているフィールドです。
通常0を設定します。
- フラグ
- URG(Urgent Pointer)
-
このビットが1だと、緊急に処理しなければならないデータがTCPセグメントに含まれていることを示します。
緊急に処理する必要があるデータ自体は、後述の緊急ポインターのフィールドに設定されます。
緊急モードに関する規定は、行われていないことから実装するシステムの開発者に任されています。
実際に使用しているアプリケーションプログラムはほとんどないようです。
- ACK(Acknowledgment)
-
確認応答番号フィールドが有効な場合は、このビットに1が設定されますが、このビットにはほとんどの場合1が設定されます。0が設定されることはほとんどありません
ただし、TCPコネクション確立の際、最初のTCPセグメントだけは違います。
最初のTCPセグメントの場合は、確認応答番号フィールドが意味を持たないため、このビットは0になります。
- PSH(Push Function)
-
このビットが1の時、受信したTCPセグメントを上位のアプリケーションプログラムに引き渡すことになります。
0の時は、引き渡さずバッファリングされます。
上位のアプリケーションプログラムがTelnetのような端末プログラムの場合は、条件にもよりますが、このビットを1にした方がアプリケーションプログラムの応答性が良い場合もあります。
逆に、上位のアプリケーションプログラムがブラウザや電子メールソフトなどの場合は、このビットを0にすることで一旦バッファに溜める方が処理性能を向上させることができます。
- RST(Reset the connection)
-
コネクションを中断したい時にこのビットを1に設定します。
受信側では、コネクションを拒否し、強制終了する必要があります。
- SYN(Synchronization)
-
コネクションを通すことを要求する時、このビットを1に設定します。
- FIN(Finish)
-
TCPセグメントの転送を終了する時、このビットを1に設定します。
- ウィンドーサイズ
-
ウィンドーサイズは、RWINとも呼ばれています。
ウィンドーサイズは、後述のスライディング・ウィンドーやフロー制御に使用します。
ウィンドーサイズは、どのくらいデータを受信できるかを示すゆとりのようなものです。
ウィンドーサイズは、TCPの伝送効率に大きく影響するため、適切なサイズに設定されていないと、十分な通信速度が出ません。
ウィンドーサイズが小さいと、送信側と受信側の間で確認応答が行われる場合、送信側で待ち時間が発生する回数が増加し、確認応答のTCPセグメントが頻繁に転送され、オーバーヘッドが大きくなり、全体のデータ転送効率が低下しま。す。
逆に、ウィンドーサイズを大きくすれば全体のデータ転送効率が向上します。
しかし、ウィンドーサイズが大きいと、回線状態が悪いネットワーク環境では、エラーが頻発し、再送信しなければならないデータ量が増加し、かえってデータ転送効率が低下してしまう結果になる場合があります。
ところで、古いWindows®を使っている場合、ウィンドーサイズやMTUの初期設定値が非常に小さいです。
最近のWindows® XP、Windows® 2000(SP3)などでは、MTUが1500オクテット、ウィンドーサイズが64240オクテットと、十分な値なので問題ありません。
しかし、Windows® MeやWindows® 98などの古いWindows®では、特にウィンドーサイズが小さいので、レジストリの設定を変更する必要があります。
なお、MTUに関しては、後述の「TCPオプションとパディング」を参照して下さい。
- チェックサム
-
TCPセグメントのチェックサムがここに設定され、データの誤り検出に使われます。
チェックサムは、送られてきたにTCPセグメントの内容が破壊されていないかどうかを確認することを目的としてあります。
具体的には、データの誤りをチェックするために使われる値です。
データの誤りの検査のための決まった計算方法があります。
この計算を行って検査を行うことで、データの信頼性を保証します。
チェックサムの計算を行うために、TCP擬似ヘッダー(Pseudo Header)という以下の表に示した特殊なヘッダーを使用します。
TCP擬似ヘッダーの先頭からの位置 | 長さ(オクテット) | 項目内容 |
0 | 4 | 送信元IPアドレス |
4 | 4 | 宛先IPアドレス |
8 | 1 | パディング(未使用:0) |
9 | 1 | プロトコル番号(TCPを表す番号:6) |
11 | 2 | TCPセグメントの長さ |
送信側では、チェックサムの計算の際に、一時的にこのTCP擬似ヘッダーをTCPセグメントの先頭に入れます。
TCPセグメントの先頭には、当然本物のTCPヘッダーがあります。
次に、TCP擬似ヘッダーとTCPセグメントのサイズの合計を16ビットの倍数にする必要があります。
そこで、半端な数になった場合、TCPセグメントの最後に0を追加しておきます。
一方、TCPヘッダーのチェックサムのフィールドには0を設定しておき、16ビット単位に1の補数和を計算をします。
次に、1の補数和を計算した結果を反転してチェックサムのフィールドに設定します。
送信側はこれだけです。
次に、受信側です。
受信側では、TCPセグメントを受信し、IPヘッダーにあるIPアドレスを取り出して、TCP擬似ヘッダーに設定し、TCP擬似ヘッダーを完成させます。
そして、チェックサムの再計算を行います。
最終的にすべてのデータを合計した値が0になっていれば、正しい内容のTCPセグメントを受信したことになります。
以下のページで、IPヘッダーのチェックサムの例ですが、プログラムで実際にチェックサムの計算を行うための方法を解説しました。
チェックサムの具体的な計算方法の紹介
IPヘッダーのチェックサムの計算でも、1の補数和が利用されているので計算方法はだいたい同じです。
対象ヘッダーやデータが違うくらいです。
1の補数和についての説明も「チェックサムの具体的な計算方法の紹介」のページに書かれています。
- 緊急ポインター
-
緊急性がある情報の通知に使われます。
例えば、処理の中断を通信相手に要求したい場合などがあります。
- TCPオプションとパディング
-
TCPオプションは、可変長で任意の個数のオプションを設定できます。
代表的なTCPオプションにMSS(Maximum Segment Size)があります。
MSSは、TCPセグメントの最大サイズのことで、MTUから40を引いた値です。
例えば、イーサーネットでは、MTUが1500オクテットなので、これからIPヘッダー(20オクテット)とTCPヘッダー(20オクテット)を際し引きます。
そうすると、MSSは1460オクテットになります。
MTUとは、Maximum Transmission Unitの略で、1回で送信可能なパケットの最大値のことを言います。
MSSの値は、MTUに基づいて自動的に決定されます。
ネットワークの途中にもっと小さなMTUのネットワークが存在すると、MSSの値をもっと小さくしなければなりません。
MSSに関しては、RFC879に記載されています。
TCPやTCPオプションについては、RFC793に記載されています。
RFCの詳細は、以下のサイトで御覧下さい。
IETF RFC Page
JPNIC RFC-JP(社団法人:日本ネットワークインフォメーションセンター)
RFC793の日本語訳
MSSのオプション長は4オクテットです。
TCPヘッダーは、32ビットの倍数で区切られています。
そこでこの長さになるまでTCPオプションの後ろの方は、パディング(padding)という0を設定したフィールドが付きます。
以下に代表的なTCPオプションの一覧を表にまとめておきました。
タイプ | 意味 |
0 | TCPオプションの終了。
オプション長は1オクテット。
|
1 | オペレーションなし。
特別な意味を持たず、TCPヘッダーを32ビット単位に揃えるためにある。
オプション長は1オクテット。
|
2 | MSSの値。
受信可能なTCPセグメントの最大サイズ。
オプション長は4オクテット。
|
3 | ウィンドースケール
64KBより大きいウィンドーサイズを使用したい時に指定します。
これについては、以下のRFC1323で記載されています。
RFC1323
しかし、前述のWindows® XPなどではウィンドーサイズの初期値が64240オクテットになっていて、64KBを超えておらず、この程度で十分なので、このオプションが使われることはほとんどありません。
|
- TCPにおける伝送制御手順
-
【図1】スリーウェイハンドシェイク
上の【図1】を御覧下さい。
TCPでは、まず相手のコンピュータと接続をする際に、【図1】のような手順で接続します。
接続を開始したいコンピュータがあった場合、(1) SYNというTCPセグメントを最初に送信します。
この中身のTCPヘッダーを見ると、SYNのフラグがセットされています。
次に、これを受信したコンピュータは、(2) SYN+ACKを送り返してきます。
これを受信したコンピュータは、確認応答として(3) ACKを送信します。
この3段階の通信で接続処理が完了します。
この手順をスリーウェイハンドシェイクと言います。
すいている回線ならあっという間に終わりそうですが、混雑している回線では、接続に時間がかかる場合があります。
【図2】コネクションのクローズ処理
TCPでは、通信の後処理もまた丁寧です。
こんなに何度もTCPセグメントを送信しあわなくても良さそうに思えますが、接続時より多い4つもTCPセグメントを送受信します。
接続を終了したいコンピュータがあった場合、(1) FINというTCPセグメントを最初に送信します。
この中身のTCPヘッダーを見ると、FINのフラグがセットされています。
次に、これを受信したコンピュータは、(2) ACKを送り返してきます。
連続して、もう一つ(3) FINを送ってきます。
この二つのTCPセグメントを受信したコンピュータは、最後に確認応答として(3) ACKを送信します。
この4段階の通信でクローズ処理が完了します。
最後に、スライディング・ウィンドーについて説明します。
【図3】スライディング・ウィンドーを使用した場合と使用しない場合の処理の流れ
上の【図3】を御覧下さい。
左側がスライディング・ウィンドーなしの場合で、右側がスライディング・ウィンドーありの場合です。
スライディング・ウィンドーなしの場合は、データを1個送っては肯定応答のTCPセグメント(上の図のAck)を返します。
最後のデータを送って最後の応答を受け取るまでキャッチボールのような動作が続きます。
大量のデータを送る必要がある場合、何度も送らなければならないので、非常に非効率的です。
たくさん送らなければならないデータがある場合、下位のMACフレームのサイズに制限があるため、上位のIPパケットやTCPセグメントも当然分割されることになります。
そこで、スライディング・ウィンドーありの場合のようにします。
この場合は、最初のデータを1個送ってもAck1を待たずに、次のデータを送ります。
ただし、上の図は一例にすぎず、このように決められているわけではありません。
上の例では、青い★マークのデータまでがAck1を確認せずに送信できるということになります。
つまり、★マークのデータまで確認応答を待たずに送れることになります。
この最後のデータまでの合計サイズがウィンドーサイズの値になります。
上の例では、Data2を送ったところまででウィンドーサイズを満たしたことになります。
例えば、上の図の例よりもっと大きなウィンドーサイズだったとして、このサイズがTCPセグメント3つ分に相当したとしたら、上の図で説明するとData3まで一気に送信できることになります。
その後、Ack1が確認応答として返されます。
TCPヘッダーに含まれるウィンドーサイズの値が一気に送信できるTCPセグメントの数と関係します。
TCPでは、このウィンドーサイズを調整することで、確認の応答が来るのを待たずにTCPセグメントを続けて送れるようになっています。
ウィンドーサイズは、確認応答が来るまで送り続けることが可能なデータサイズとなります。
つまり、何オクテットまで送り続けることができるかをこのウィンドーサイズに設定された値が示しているわけです。
この値は、ただ大きければ良いというわけではなく、利用する回線の状態にもよります。
低速な回線やいつも混雑している回線では、小さめな値にするのが理想的です。
このような状況以外なら大きい値が理想的です。
ウィンドーとは、受信側で用意しなければならないバッファを指し、ウィンドーサイズとはそのバッファのサイズを意味します。
ウィンドーサイズは可変長で、自由に調整できます。
このウィンドーサイズを動的に変化させることをスライディング・ウィンドーと言います。
ウィンドーサイズのフィールドは16ビットなので、これを越える値の場合はウィンドースケールというTCPオプションで指定します。
では、このウィンドーサイズをいつ相手に通知するのでしょうか。
「私のウィンドーサイズは〜だけど、あなたのウィンドーサイズは幾つですか。」と接続時に送信側が受信側に尋ねることになります。
【図1】の(1) SYNのTCPセグメントを送る際、その中身のTCPヘッダーを見ると、SYNのフラグがセットされて、ウィンドーサイズに送信側のウィンドーサイズの値が設定されています。
これで受信側に送信側のウィンドーサイズを通知できます。
このTCPセグメントの戻りとして来る(2) SYN+ACKのTCPセグメントの中身にあるTCPヘッダーに含まれるウィンドーサイズには、受信側のウィンドーサイズの値が設定されています。
これでお互いのウィンドーサイズがわかったことになります。
後は相手に合わせてウィンドーサイズの値になるまでTCPセグメントを連続して送っては確認応答を受け取れば良いことになります。
このように、TCPでは、スライディング・ウィンドーを採り入れることで、確認応答を待たずに一度にたくさんのデータを連続して伝送することで効率的な通信を行っています。
- TCPの処理詳細
- フラグ処理
-
TCPヘッダーに含まれるフラグは、通信を行う両サイドのコンピュータが受け取った後何をしたら良いかを示します。
これによって、どのような伝送制御手順で通信を行うかをお互いが理解し合えます。
各フラグの役割に関しては、上の方に書いたTCPヘッダーのフラグの項目を参照して下さい。
- ウィンドー処理
-
前述のTCPヘッダーのウィンドーサイズの項目での説明と、上記のスライディング・ウィンドーの説明を参照して下さい。
また、ウィンドー処理は、フロー制御との関係もあるので、以下のフロー制御と輻輳制御の項目も参照して下さい。
- フロー制御と輻輳制御
-
まず、フロー制御について説明します。
TCPは、フロー制御をウィンドーサイズを動的に変更することで実現しています。
送信先から送信元に送信される確認応答のTCPセグメントに変更後のウィンドーサイズの値が含まれているので、送信元はこの値に基づいて送信するデータ量を調整しています。
受信側は、受信したデータを一旦受信バッファの中に入れて、これをソケットインターフェイスと呼ばれるAPI(Application Programming Interface)を通じてアプリケーションプログラムに渡します。
しかし、何事もいつもうまくいくとは限りません。
アプリケーションプログラムは四六時中通信処理をしているわけではありません。
様々な周辺機器とデータのやりとりをしています。
計算をずっとやっている時もあります。
繰り返しが多いCPU内部の処理だけを集中的に行っている場合もあります。
従って、アプリケーションプログラムがビジー状態の時もあり、アプリケーションプログラムが処理中に作成者のミスから発生するバグと呼ばれる不具合のせいなどが原因して処理を続行できなくなる場合もあります。
このような状態がずっと続くと、受信バッファにどんどんデータが溜まり、最終的にバッファの空きがなくなり、オーバーフローしてしまうことがあります。
こうなってしまうと、受信側はこれ以上データを受信してバッファに溜めることができなくなり、ウィンドーサイズの値を変更して送信側に通知し、送信側からの送信データの量を制御することができます。
前述のように、受信バッファの空きがまったくなくなった場合、何も受信できなくなるので、ウィンドーサイズに0の値を設定して送信側に通知し、送信を止めます。
次に、輻輳制御の説明に移ります。
輻輳は、ふくそうと読みます。
ネットワークは、利用者が殺到して混雑することがあります。
ネットワークは、回線の速度や伝送能力によっても左右されます。
回線の能力が低いと、すぐ混雑してしまう場合があります。
また、ネットワークの途中にあるルーターは、まるで道路の交差点のようなもので、ここも混雑します。
ルーターの処理能力にもよりますが、性能が悪いルーターだったり、株などのインターネット取引が限られたサーバーに集中し、このサーバーへの経路上にあるルーターの処理が追いつかなくなったりします。
回線の伝送能力を上回るくらいデータを送ったり、ルーターがTCPセグメントを処理したり転送したりできなくなるほど処理が追いつかなくなり、途中で頻繁にTCPセグメントが破棄されるような状態が発生することがあります。
このような状態を輻輳(congestion)と言います。
一度輻輳になると、この状態から抜けることが困難になります。
TCPでは、途中でTCPセグメントが失われると、再送を試みます。
そして、何度もTCPセグメントロストが発生すると、再送が頻発します。
このままでいると、最悪送ってはロストという悪循環に陥り、ネットワークがダウンしてしまいます。
このことを輻輳崩壊と言います。
これを回避し、問題なく通信を行うための手段がいろいろ考案されました。
幾つか、アルゴリズムが考案されました。
このことは、以下に示したRFC2001にまとめられています。
RFC2001の日本語訳
RFC2001の原文
RFC2001の原文の冒頭に「TCP Slow Start, Congestion Avoidance, Fast Retransmit, and Fast Recovery Algorithms」と書かれていますが、これは、「スロースタート及び輻輳回避、即時再送及び即時回復のアルゴリズム」という意味です。
これらのアルゴリズムについては、上記のRFC2001の日本語訳が参考になります。
この訳文の内容はなかなか良いと思います。
これを読むと、輻輳制御について、だいたいわかると思います。
インターネットでも、輻輳制御について簡単に説明しているサイトは幾つかあります。
- TCPオプション処理
-
TCPオプションに関しては、TCPが実装されているシステムに依存します。
実装されていないTCPオプションは、処理できないので正しく機能しません。
つまり、パソコンやそれ以外のコンピュータ、組み込み機器、OSなど実装系が異なるとサポートされているTCPオプションも異なります。
TCPオプションに関する詳細は、前述の「TCPオプションとパディング」を参照して下さい。
ページトップへ
トランスポート層のプロトコル[メニュー]へ戻る
サイトマップ(LAN技術研究室の案内図)
ネットワーク技術用語集へ行く
LAN技術研究室のトップページへ
© 2007 Toyozi Masuda All rights reserved.