TCP(Transmission Control Protocol):Tech Basics/Keyword
TCP/IPのうち、「信頼性のある通信」を実現するTCPについて解説。広く利用されているTCPだが、その通信の仕組みは?
「TCP/IP(Transmission Control Protocol/Internet Protocol)」は、コンピュータやアプリケーション同士の通信のために、現在最も多く使われている基幹ネットワークプロトコルである。本記事ではTCP/IPを構成するプロトコルのうち、TCPについて解説しておく。
Webサーバとブラウザ、メールサーバとメールクライアント、Webアプリケーションとスマホのアプリなど、インターネット上で利用されているほとんどのアプリケーションは、このTCPによる通信を利用している。
信頼性のある通信を実現するTCP
「TCP(Transmission Control Protocol)」は2つのアプリケーション間で、「信頼性のあるストリーム型の通信」を行うためのプロトコルである。
「信頼性のある通信」とは、送信したデータが送信した順番で正しく相手にまで届くことが保証され、何らかの理由で送信が失敗した場合には自動的に再送信するなどして、確実に相手にデータが届くことを保証する通信のことである。
送信したデータが送った通りに相手に届くのは当たり前と思うかもしれないが、実際はそうではない。ネットワークが混雑していると、例えば次のようなトラブルが生じがちだ。
- ネットワークの混雑などの影響で、通信経路の途中でパケットが大幅に遅延する
- 負荷分散や経路制御などによって、送信順とは異なる順番で届く
- パケットの一部がエラーで化ける
- (ハブやルーター、ネットワーク機器のリソース不足などで、パケットそのものが廃棄される
このような状況を検出し、必要ならデータの並べ替えや再送処理を行って、正しいデータを相手に届けるのが「信頼性のある通信」である。またネットワークの構造や媒体、帯域幅、遅延時間などに依存せず、どのような構成でも同じように利用できることが求められている。
TCPの機能や特徴は次の通りだ。
- アプリケーション間で双方向の通信路を開設し、それを使って相互に通信する
- 「信頼性のある通信」機能を提供する
- 通信経路が混んでいる場合は送信量を抑えたり、逆に空いている場合はまとめて送信して送信効率を上げたりする
アプリケーション間での双方向の通信路の実現
TCPで通信をするためには、通信相手のIPアドレスとポート番号(ポート番号は16bitの整数値)を指定してサーバに接続要求を開始する。例えば、Webサーバとの通信に使われるHTTPでは、TCPの80番か443番(暗号化する場合)のポートを使って通信する(サーバ側のTCPのポート番号はアプリケーションごとにほぼ決まっている)。
これを図にすると次のようになる。
TCPによるアプリケーション間通信
TCPでは、アプリケーション間で仮想的な通信路を開設して、その中で信頼性のある通信を行う。ただ通信路の開設や終了処理、データを送信するたびに相手に届いたかどうかの確認や失敗時の再送信などの処理が必要なため、通信のコストは低くはない。
TCPの動作を確認するには、Telnetなどを使って接続先のサーバとポート番号を指定して接続してみるとよい。
- TIPS「WindowsのTelnetクライアントの使い方」
確認応答を使って信頼性のある通信を実現する
TCPでは、下位のプロトコルやネットワークとして、IPや「イーサネット」、無線LANなどを利用している。これらはベストエフォート(最善努力)型であり、送信した相手にデータが必ず届くという保証はない。ネットワークが混雑していると、大幅に遅延したり、場合によっては途中で破棄されてしまう。
そんな特性のネットワーク上でも信頼性のある通信を実現するために、TCPではデータを送信するたびに必ず「確認応答(ACK:Acknowledge)を返信する」という手法を使っている。相手にデータを送信後、相手から必ずACKが返ってくるのを待ち、もし返ってこない場合は再度データを送り直す。
確認応答による信頼性のある通信の実現
イーサネットや無線LANのようなベストエフォート型の通信媒体では、ひどく混雑しているとパケットが破棄され、相手にまでデータが届かないことがある。そんな状況でも通信できるように、TCPではデータの送信に対して確認応答(ACK)の返信を要求する。送信したデータに対してACKが返ってくれば次のデータを送信するが、一定時間以内に返ってこない場合は同じデータを(何度も)再送し、確認応答が返ってくるのを必ず待つ。
シーケンス番号による送受信データの管理
TCPで送信するデータの順番や並びは「シーケンス番号」という32bitの整数値で管理されている。例えば「シーケンス番号12345から2000bytes分」というフラグを付けてデータを送信する。すると受信した側では「シーケンス番号12345から1000bytes分受け取り完了。次は13345から受信」というふうに確認応答を返す。この場合は半分しか届いていないようなので、「13345から1000bytes分」を再送信することになる。そして「14344まで受信完了」というACKが返ってくれば送信は完了となる。
シーケンス番号による送信データ管理
送信するデータには1byteごとにシーケンス番号という32bitの数値が割り当てられている(初期値は任意。2の32乗を超えると0に戻る)。どの位置のデータまで送ったか(受け取ったか)は、このシーケンス番号でやりとりしている。受け取ったデータの(最後の位置の)シーケンス番号を返すことにより、どこまで送信が完了したかが分かる。
ウィンドウ制御による効率的な通信の実現
確認応答を使うことにより、確実な通信を実現できるが、これだけでは非常に効率の悪い通信しかできない。例えばネットワークの遅延が大きくて、パケットが1往復するのに100ミリ秒程度かかるサイトがあるとする(日本から海外へ接続する場合などに相当)。1KB程度のパケットを1つ送るたびに確認応答を1つ待つとすると、1秒で10パケット分しか送信できない(≒約10KB/s)。たとえ回線速度が100Gbit/sあったとしても、この速度しか出ない。
これでは効率が悪過ぎるので、TCPでは「ウィンドウ制御」を採用して、効率向上や輻輳(ふくそう)制御(混雑時の送信抑制制御)などを行っている。これは、ある程度の量のデータ(パケット)をまとめて送信し、それに対して受信確認を1つだけ送るという方式である。一度に受け取り可能な最大量を「受信ウィンドウサイズ」という。
例えばウィンドウサイズを1MBにすると、最大で1MB×10となり、約10MB/sで送信できることになる(TCPを1コネクションしか使わない場合)。このように、ウィンドウ制御は通信回線を効率よく使うには必須の技術である。
TCPのウィンドウサイズは通信状態によって動的に変わる。もっとパフォーマンスを上げたい場合はサイズを大きくするし、受信側の処理が追いつかなかったりリソースが不足したりしている場合は、もっと小さくして送信を一時的に待ってもらう、といった制御もする。
TCPのウィンドウ制御
TCPの受信確認は1パケット受信するごとに行うのではなく、ある程度のデータがたまってから送る。送信側は、ウィンドウサイズいっぱいまでなら先行してデータを送信してもよい。受信側は適当なタイミングで受信確認を返す。受信側には順不同でパケットが届くことがあるが、ウィンドウの先頭の方から順にACKを返していき、その分だけウィンドウの開始位置をずらす。シーケンス番号上を受信ウィンドウが移動していくように見えるので、スライディングウィンドウとも言う。TCPの受信ウィンドウサイズを大きくしておくと、ネットワークの遅延(ラウンドトリップタイム)の影響を抑えて、まとまった量のデータを効率よく送受信できる。ウィンドウサイズは最初は小さいが、通信量が増えるにつれて拡大したり(スロースタートアップ。無用なトラフィック増加を抑える効果がある)、混雑時や再送の発生時には縮小させたりするなど、状況に応じて適切に制御される。
TCPの状態遷移図
TCPではコネクション(接続)ごとに、それぞれシーケンス番号やACK番号、オープン/クローズなどの状態といった内部的な「ステート(状態)」を持っている。TCP接続のオープンやクローズ、確立などに伴って、次の図のように状態が変わる。
TCPの状態遷移図
TCPは「状態」を持つプロトコルである。TCPの接続を待ち受けしている方(LISTENしている方)を「パッシブオープン」、そのサーバへ接続する方を「アクティブオープン」という。オープン処理が完了するとお互い「ESTABLISHED」状態になり、データの送受信が可能になる。通信が終了するとお互いに通信路を「クローズ」して終了する。システム内に現在TCPコネクションが幾つあるか、リッスンされているポートは何か、などの情報は、Windows OSならnetstatコマンドで確認できる。TIPS「netstatコマンドを使いこなす」参照。
この図の最初の部分にあるオープン処理時のパケットの流れを図にすると次のようになる。
TCPのオープン処理
TCPの接続開始時にやりとりされるパケットの流れ。TCPの接続を待ち受けしている方(サーバ)をパッシブオープン、そのポートに対して接続要求を送信している方(クライアント)をアクティブオープンという。SYN(Synchronize。同期)は接続開始の指示のこと。なぜSYNかというと、双方が保持しているシーケンス番号カウンタを同期させる指示だからだ。このように、TCPの接続開始時には3回パケットが往復するので、3ウェイハンドシェークと言う。
オープンしようとしている側からTCPの接続要求(SYN)を送信すると、待ち受けしている側(パッシブオープン側)はSYNに対する確認応答(ACK)を送信する。と同時に、自分の側からも相手に対してSYNを送信する。受け取った側では、そのSYNに対するACKを送信して、オープン処理が完了する。双方からSYNを送るのは、TCPが全二重の通信路だからだ。
オープン処理ではこのように3回パケットが往復するだけだが、クローズの場合は、どちらから先にクローズ要求を出すかで処理が変わり、もう少し複雑になる。
このように、TCPはプロトコルスタック内部に状態を持っているし、受信ウィンドウ用のバッファも持たなければならないなど、割と「重い」処理が必要なプロトコルである。
TCPは受信確認やウィンドウ制御などを使って信頼性のある通信を実現しているが、そのために処理が重くなっているし、特に混雑したネットワークではリアルタイム性などが損なわれることもある。現在のネットワーク技術はTCP/IPの誕生時よりもはるかに高速化・複雑化しているが、それらに対応するべく、TCPの仕様や実装方法の改良なども続けられてきた(例えば当初のTCPでは、ウィンドウサイズは最大64KBまでだった。これは現在の高速ネットワークでは小さ過ぎる)。今後もTCPの重要性は変わらないだろう。
■関連リンク
- 基礎から学ぶWindowsネットワーク 第14回「信頼性のある通信を実現するTCPプロトコル」
- Windowsネットワークの基礎 第6回「TCP/IPの概要」
- TIPS「netstatコマンドを使いこなす」
- @IT eBook「Windows管理者のためのIPv6入門」電子書籍版
Copyright© Digital Advantage Corp. All Rights Reserved.