この記事では、TCP の基礎となる原理やアプリケーション・アーキテクチャなどを詳しく紹介し、高性能なサーバを構築するための TCP の使用方法について説明しています。
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。
TCPの機能
TCPは、信頼性の高い全二重通信をユーザプロセスに提供する接続指向のプロトコルです。このようにして、信頼性の高い整然としたデータパケットを確保し、トラフィック制御をサポートすることができます。ここでは、以下の側面から始めて、なぜTCPが上記のような動作を実装すべきなのかを説明します。
1、IPネットワーク層がデータパケットの信頼性を確保できない理由
2、TCPが到達可能で整然としたデータパケットを確保する方法
3、TCPがトラフィック制御をサポートする方法
4、TCP の状態とアプリケーション
OSI ネットワークレイヤ
IPネットワーク層ではデータパケットの信頼性が確保されない理由を理解するために、まずOSIネットワーク層を見てみましょう。以下の層では、TCPはトランスポート層に位置しており、プロトコルの信頼性と継続性を確保しています。具体的な送受信パケットは、その下にあるリンク層や物理層によって決定されるので、TCPの仕事もその下の最適化や改善の上に成り立っています。
クライアントとサーバ間の通信はアプリケーションプロトコルを使用します。トランスポート層での通信はTCPを使用し、TCPは下位層のIPを使用し、IPは何らかの形のデータリンク層を使用して通信を行います。
ネットワーク内のデータは、最終的に複数のルータ接続を介して伝送されることがわかっています。基礎となるイーサネットプロトコルは、電子信号がどのようにデータパケットを形成するかを規定しており、これはローカルエリアネットワーク(LAN)のポイントツーポイント通信の問題を解決しますが、複数のLANの相互通信の問題を解決することはできません。
ネットワーク層で使用されるIPプロトコルは、独自のアドレス規則のセットを定義しており、主に相手のIPアドレスに応じて情報を送信するための最適な経路を見つけるためのアドレッシングとルーティングの問題を解決します。LANはルータを介して接続されており、IPプロトコルに基づいてパケットを特定のルーティングインターフェースに転送するように指示します。しかし、IPプロトコルは、パケットの到着と完全性を確保するものではなく、特にネットワークが輻輳している場合には、データ伝送効率を確保するために、一部のパケットは廃棄されることになります。
データパケットの完全性、整然性、信頼性を確保するために、TCPではこのようなことが行われています。
TCPへの深堀り
TCP パケットの構成
多くのネットワークでは、リンク層でのデータフレームの制限である最大伝送単位(MTU)が設定されています。例えば、イーサネット上でのMTUは1,500バイトです。IP データグラムはイーサネットで送信されます。その長さがMTU値よりも大きい場合、各シャードの長さがMTUよりも小さくなるように、シャードで送信する必要があります。
また、データパケットは、それ自身のTCPヘッダに加えて、IPヘッダ情報とイーサネットヘッダ情報を含むヘッダ情報を含みます。IPパケットは、イーサネットデータパケットの負荷に少なくとも20バイトを必要とします。そのため、IPデータパケットの負荷は最大で1480バイトとなります。
では、TCPパケットのサイズはどのくらいなのでしょうか?
これを決めるにはMSSの値が必要になります。MSSはTCPにおける概念です(ヘッダーのオプションフィールドにあります)。MSSとは、TCPのデータパケットが毎回送信できるデータセグメントの最大値です。TCPパケットの長さがMSSより大きい場合、セグメント単位で送信する必要があります。MSSが設定されていない場合、デフォルト値は536バイトです。つまり、1つのTCPパケットは約500バイトです。
信頼性の確保
上述したように、基礎となるルータは、パケットを転送する際に、パケットの信頼性や順序性を確保していません。
まず、パケットの整合性を確保するために、TCPは、MSSに基づいてMSSよりも大きいパケットをサブパッケージ化します。デフォルトのMSSは563バイトであり、これはネットワーク層でシャードされることからパケットにMUTよりも小さいです。
次に、SEQとACKを追加し、パケットの信頼性を確保するためにタイムアウト再送の仕組みを採用しています。
SEQ
パケットの順序性を確保するために、TCPは各パケットにシーケンス番号(SEQ)を割り当てます。これにより、受信側はパケットを順番に復元することができます。また、パケットが失われた場合には、どのパケットが失われたのかを知ることができます。一般的に、最初のパケットのSEQは乱数であり、1から始めることもできます。
ACK
SEQが割り当てられたので、パッケージの到着をどうやって確実にするか?
これはACKに基づいて決定されます。パケットを受信するたびに、送信者が送信されたことを確認できるように、受信者はACKを返さなければなりません。さらに、受信機は各パケットを検証しなければなりません。検証中にエラーが発見された場合、ACKは送信されず、送信者のタイムアウト再送をトリガします。
ACKには以下の情報が含まれています。
- 次のパケットを受信する予定の SEQ
- 受信機の受信窓の残容量
wireshark を使用して oschina パケットをキャプチャし、3 者間ハンドシェイクのデータを確認しています。
Native IP: 192.168.1.103 oschinaIp: 116.211.174.177 Three-way handshake process: 1.me->osChina:syn=1 seq=x ack=0 2.osChina->me:syn=1 seq=y ack=x+1 3.me->osChina:seq=x+1 ack=y+1
1、me->osChina:syn=1 seq=0 ack=0
2、 osChina->me:syn=1 seq=0 ack=0+1
3、 me->osChina:seq=0+1 ack=0+1
三者三様のプロセスを比較してみましょう。
再送タイムアウト
ネットワークが非常に不安定であることはわかっています。データパケットにSEQやACKを付加して秩序を確保したとしても、パケットロスやタイムアウトなどの問題が発生しないという保証はありません。送信者が送信したデータや受信者が返したACKがネットワーク上で紛失したり、タイムアウトしたりした場合はどうすればよいのでしょうか?
RTO、Retransmission TimeOutです。パケットがタイムアウトしたかどうかを判断するには、評価方法が必要です。RTTは、与えられた接続の往復時間を測定します。ネットワーク・トラフィックの変化により、時間はそれに応じて変化します。TCP はこれらの変化を追跡し、RTO を動的に調整する必要があります。
送信者が一定時間内にパケットのACKを受信しない場合、パケットがネットワーク内で失われたと判断され、パケットが自動的に再送されます。この仕組みを再送タイムアウトと呼びます。
この期間内に、送信者が受信者からのメッセージが失われたためにACKメッセージを受信しなかった場合、送信者はパケットを受信者に再送します。タイムアウトタイマ後に送信者がこのパケットのACKメッセージを受信したが、タイムアウトのために送信者が既にこのパケットを再送している場合、送信者はこの時点でACKを処理せず、単に廃棄します。受信側は、このパケットを受信した後、再度ACKメッセージを返します。
トラフィックコントロール
以上のことから、TCPはデータの信頼性を確保できることがわかりますが、効率性にも配慮しなければなりません。考慮する必要があるのは、以下の3点です。
1、パケットを一括して送信するためのサポート
2、ネットワーク状況に応じた輻輳制御をサポート
3、レシーバーの状態を把握して、レシーバーに負担のないようにする機能
以上の3つの要件を踏まえ、以下のような対策を講じています。
スライディングウィンドウ
TCPパケットを1パケットずつ送信して確認すると効率が悪すぎます。信頼性は確保されていても、1パケットずつの送信・確認では効率が確保できません。このような場合、一括して送信・確認する方法が必要になりますが、それがスライディングウィンドウです。
スライド式の送信ウィンドウ:
送信ウィンドウでは、左から順に、このウィンドウの前のデータは、受信者が送信して確認したデータでなければならず、送信ウィンドウ内に入るデータは、送信者が送信可能なデータであり、送信ウィンドウの後のデータは、送信できないデータです。
タイムアウトやロスが発生した場合、2つの解決策が提案されています。
1、Go-Back-N。失われたパッケージのSEQに続くSEQを持つすべてのパケットが再送されます。
2、紛失したパケットのみを送信するARQを選択し、重複を避けて送信する(効率が高く、重複パケットの送信を防ぐことができます。
また、スライディングウィンドウは、送信者に受信機の処理状況を知らせる機能も持っています。TCP受信機のキャッシュが一杯でそれ以上のデータを処理できないが、送信者はそれを知らないと仮定すると、この場合、送信者は現在のスライディングウィンドウのサイズを送信者に知らせることで、それ以上のデータを送信しません。この場合、パケットを送信するたびに現在のスライディングウィンドウのサイズを送信者に通知することを条件に、送信者はそれ以上のデータを送信しません。
1、また、受信者はデータを受信した直後にACKを送信するが、同時に送信者にウィンドウのサイズを0と宣言します。
2、また、キャッシュに十分な空き容量があるまでは、パケットが到着してもすぐにACKは送信されません。これにより、送信者がウィンドウをスライドさせることを防ぐことができます。しかし、問題もあります。受信側がACKを送信する際の遅延はタイムアウト時間を超えてはなりません。長すぎると、送信者がデータが失われたと勘違いして再送してしまう可能性があるのです。
輻輳制御
ネットワークの状況が不安定であることを知っています。良いケースでは、より多くのパケットを送信することができます。悪い場合には、パケットの送信レートが変わらないと、ネットワークの負担が大きくなるだけでなく、パケットが多すぎてロスが発生し、タイムアウト再送が多くなり、通信効率が低下することは間違いありません。
これを踏まえて、TCP通信の両当事者は、ネットワーク内の輻輳率に依存する輻輳ウィンドウ(cwnd、輻輳ウィンドウ)と呼ばれる値を保持しており、送信側の送信ウィンドウの値は、輻輳ウィンドウの大きさと等しい値となっています。ネットワーク内に混雑が発生していない場合、送信者がより多くのデータをネットワークに送ることができるように、混雑ウィンドウの値を大きくすることができます。そうでなければ、ネットワークの混雑率を増加させないように、混雑ウィンドウの値を減少させます。
TCPは現在、輻輳制御のために以下の4つの主要なアルゴリズムを持っています。
1、スロースタート
2、輻輳回避
3、高速再送信
4、高速回復
具体的なアルゴリズムの実装は紹介しません。大まかに実装されている機能は、ネットワークに負荷がかからないように、現在のネットワークの状況から適切な送信レートを見つけることです。例えば、Slow Startとは、最初は送信速度が遅く、パケットロスが発生した場合にそれに基づいてレートを調整することを意味します。パケットロスが発生していない場合は、送信速度を高速化します。パケットロスが発生した場合は、送信速度が低下します。
TCPステータス
TCPユーザなら誰でも知っていることですが、TCPが接続を確立するときには3ウェイ・ハンドシェイク、接続が切断されるときには4ウェイ・ハンドシェイクが発生します。では、どのような状態になっているのでしょうか?
上の図は覚えておいて損はありません。下の図を見て整理して、具体的な申請状況を見てみましょう。
このように、接続が正常に確立されると、ステータスはESTABLISHEDとなります。受信側のステータスがSYN_RECVの場合は、第2のハンドシェイクメッセージに返信したことを示しており、送信側の再確認を待っている状態です。ネットワークが多数のSYN攻撃を受ける場合、SYN_RECVステータスが多数存在します。この場合、これらのIPアドレスを特定し、ファイアウォールフィルタリングを使用することで、多数の偽接続問題を解決することができます。
失われた接続 - TIME_WAIT
ネットワーク上では、一方のパーティは積極的にクローズしているが、四者間ハンドシェイクではクローズしていません。TCPで確立されたチャネルは残っているのでしょうか?どのくらいの期間でクローズされるのでしょうか?この時のTCPの状態はTIME_WAITです。現実にはこのような状況になることが多いことが想像できます。閉鎖された接続の多くは、ハンドシェイク通信ではなく、能動的に閉鎖されています。この時にクローズしている場合、以前のTCPチャネルを再接続できるのか?それとも再接続する必要があるのでしょうか?
いずれの TCP 実装でも、MSL の値を選択する必要があります。デフォルト値は2分または30秒です。TIME_WAITのデフォルト値はMSLの2倍で、持続時間は1分から4分の間です。MSLは、IPデータパケットがネットワーク内で生き残るための最長時間です。
TIME_WAITが存在する2つの理由:1.信頼性の高いTCP全二重接続が終了しているため 2.古い重複パケットがネットワーク内で消えることが許可されているため
TCPは、接続が終了した後に、接続の古い重複パケットが再生産されることを防止しなければならず、同じ接続の具現化と誤解されます。TIME_WAITが、MSLの2倍の長さである十分な長さであれば、ある方向のパケットが廃棄される前に、せいぜいMSLの間生き延びることができるようにするのに十分です。
TIME_WAIT ステータスから CLOSED ステータスに至るまで、タイムアウトの設定が存在し、それは 2 * MSL である(RFC793 では MSL は 2 分、Linux は 30 秒と定義されている)。この制限時間を超えた場合、現在の TCP チャネルはクローズ状態と定義されます。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ