ゼミ中 〜TCP(2)

先週の金曜日にやったが、書き忘れていた。

TCPのコネクションについての話は前回で終わり、今回はインタラクティブ・データのやり取りについて。

インタラクティブな通信とは何か。例えば、ファイルの転送とssh(telnet)の操作で求められるネットワークの制御=トランスポート層プロトコルの挙動は違う。ファイルの転送では大量のデータを効率よく伝送することが主目的で、双方向的な入力に対する応答性にはこだわらない。逆にsshのようなインタラクティブな通信ではファイルの転送も出来るに越したことはないが応答性の方が重要である。

まずインタラクティブ通信ではプッシュ(PUSH)フラグが意味を持つ。PUSHフラグを用いることで送信バッファが溢れるのを待たずにすぐに送信することができる。もともとファイル転送のようなバルク転送では、アプリケーションからある程度のデータがTCPにバッファされてから送信する方が、送信するTCPセグメント数を減らすことができるためTCPオーバーヘッドを減らすことができる。sshのようなインタラクティブ通信では効率性よりも応答性の方が要求されるため、PUSHフラグを用いてすぐに送信を行う。ただしPUSHフラグに対する挙動は実装依存であるため注意が必要である。

rloginリモートログインコマンドでは、キーボードから一文字入力するごとにアプリケーションからTCPに送信行為が行われる。前述のPUSHフラグによって、(Nagleアルゴリズムを用いない)すっぴんのTCPではキーボードの打鍵ごとに1つのTCPセグメントが生成され、送信される。例えば、10回のキーボードの打鍵によって、10のTCPセグメントが生成され、送信される。

さらにリモートログインでは打鍵したキーの内容をサーバからクライアントにechoする。”k”と入力したのであれば、”k”がサーバから応答される。よって以下のように1打鍵により2回のセグメントの往復が行われる。

  1. クライアントからサーバに”k”を送信する。
  2. サーバからクライアントにACKを送信する。
  3. サーバからクライアントに”k”を送信する。
  4. クライアントからサーバにACKを送信する。

これは著しく非効率である。この効率の問題を解決するために2つのアルゴリズムを紹介する。それがNagleアルゴリズムと遅延ACKである。

遅延ACK

まず、前述の4つのシーケンスを見るにあたり、2.3.のサーバからクライアントへの通信が2つ続いている点に着目する。実は3.のTCPセグメントではACKフラグがたっており、実質2.が送信されなくても問題はない。そこで、受信側TCPの処理として遅延ACKという手法をとることが提案される。

すっぴんのTCPはセグメントを受け取るにあたり、すぐにACKを返そうとする。このACKを返そうとする時間に遅延時間を持たせる。この時間はおおむね200msである(たしかRFCで決定されている)。この200ms待つ間に通信相手から他のセグメントを受け取ったり、アプリケーションから送るべきデータを受け取ったりする。もし新しいセグメントを受け取ったのであればACKを更新し、アプリケーションからデータを受け取ればそのデータも一緒に送ろうとする。そして200msのタイマーが切れたら送信を行う。このように受信応答に対してデータ送信を”便乗”させてしまう行為をpiggybackと呼ぶ。

今回の場合、サーバからのechoの送信要求にはPUSHフラグが立てられているため、以下のようなシーケンスになる。

  1. クライアントからサーバに”k”を送信する。
  2. サーバからのACK送信は遅延ACKにより200ms抑制しようとする。
  3. サーバからクライアントに”k”を送信する。PUSHフラグにより、すぐに送信する。
  4. クライアントからサーバにACKを送信する。

実際に送信しているのは1.3.4.であり、4回のシーケンスが3回に抑制されている。

以上のように遅延ACKとはTCP受信側の処理である。

nagleアルゴリズム

先ほど、rloginのようなアプリケーションは打鍵ごとにTCPに要求を出すため、「10回のキーボードの打鍵によって、10のTCPセグメントが送信される」と説明を行った。

このように送信要求ごとに小さいセグメント(例では1バイトの長さ)が生成されることによって、ネットワークの効率は悪くなる。特にLANのような高速なネットワークでは問題はないが、インターネットのようなWANのネットワークでは遅延が大きく、いくつもの小さいセグメントを回線上に載せてしまうこととなる。

このアルゴリズムで行いたいことは「インターネットのようにRTTの大きいネットワークで、いくつもの非効率な小さいパケットを送信したくない」ということである。そこで、nagleアルゴリズムでは「MSSではない小さなTCPセグメントを送出した場合、ACKを受信するまでは次の小さいセグメントを送信しない」というルールを決めた。

このルールでは、RTTの小さい高速なネットワークでは、非効率な小さいパケットは問題にならない。

例えば、RTTの大きいネットワーク上で、abcdef…と連続的に打鍵した場合は以下のようなシーケンスになる。

  1. クライアントからサーバに”a”を送信する。
  2. クライアントからサーバに”b”を送信しようとするが、”a”のACKを受信していないので送信を抑制する。
  3. クライアントからサーバに”c”を送信しようとするが、”a”のACKを受信していないので送信を抑制する。
  4. クライアントからサーバに”d”を送信しようとするが、”a”のACKを受信していないので送信を抑制する。
  5. サーバからのACK送信は遅延ACKにより200ms抑制しようとする。
  6. サーバからクライアントに”a”を送信する。PUSHフラグにより、すぐに送信する。
  7. クライアントからサーバにACKを送信するが、送信バッファにあるデータ”bcd”と一緒に応答する。
  8. クライアントからサーバに”e”を送信しようとするが、”bcd”のACKを受信していないので送信を抑制する。
  9. クライアントからサーバに”f”を送信しようとするが、”bcd”のACKを受信していないので送信を抑制する。

このようなルールを適用することによって、RTTの大きいネットワークではまとめて、RTTの小さいネットワークでは小出しに送信することができるようになる。

nagleアルゴリズムは遅延ACKとは違い、送信側の処理である。

まとめ

遅延ACKとnagleアルゴリズムは無駄なセグメントの送信を抑制するための手段である。場合によっては、これらのアルゴリズムが障害になる場合もあり、そのときには解除することも必要である(それが昔にやってた研究だったり)。

TCPのコネクションの話が終わってすぐに遅延ACKとnagleアルゴリズムを取り出してくるあたり、この本は面白い。が、初めて読む方々は辛かろう。実際、そういう顔だったし…

Leave a Comment


NOTE - You can use these HTML tags and attributes:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>