質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

Q&A

解決済

1回答

5093閲覧

Nagleアルゴリズム、write-write-writeはなぜ問題ないのか?

vc3000

総合スコア196

TCP

TCP(Transmission Control Protocol)とは、トランスポート層のプロトコルで、コネクション型のデータサービスです。

0グッド

0クリップ

投稿2016/12/31 16:23

相手が遅延ACKを有効にして自分がNagleアルゴリズムを有効にしている状態で、自分がwrite-write-readの順で読み書きをすると、

  • 最初のwriteで即座にパケットが送られる
  • 2番目のwriteは(Nagleアルゴリズムにより)ACKを受信するまで送信されない
  • しかし相手は遅延ACKを有効にしているので、ACKを送信するまで最大500msかかる

というように遅延が生じてしまうというのは理解できました。しかし、Nagleによるとwrite-write-writeの順番で書き込みした場合は問題が発生しないそうですが、その理由が分かりません。どうしてでしょうか?

ユーザーレベルでできる解決策は、ソケットに対して、write-write-read の順番で読み書きすることを避けることである。write-read-write-read は大丈夫である。write-write-write は大丈夫である。しかし write-write-read はダメである。それ故、可能なら、TCP での小さな書き込みをバッファリングして一度に一つにまとめて送信するべきである。標準的な UNIX I/O パッケージを使用し、読み込みの前に書き込みのフラッシュを行うならば通常は機能する。

https://ja.wikipedia.org/wiki/Nagle%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0

もしかしたら

ただし、最大セグメントサイズ以上のデータを受信したときは最低限2つ目のセグメントでACK応答を返さなければならない

https://ja.wikipedia.org/wiki/TCP%E9%81%85%E5%BB%B6ACK

が関係しているのかもしれないと思いましたが、最大セグメントサイズ未満のデータを受信した場合は、2個以上のセグメントを受信してもACKを返さない場合もあるのではないでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

ご質問で引用 (重引用) されたSlashdotの投稿ですが、これ、何か変です。

まず、write-write-readの場合について検討します。

投稿では、write-readの際はwriteがバッファから出て行かないとreadができないという前提を置いています。しかし、TCPは送信と受信を同時にできる全二重の通信プロトコルです。つまり、TCP層では「送信の成功を待って受信を開始する」という動作はしていません。

「送信が成功しないと受信ができない」というのはアプリケーション層での、クライアント-サーバシステムでの決まりごとです。投稿の筆者はこれと混同しているようです。それはおくとして、この場合クライアント側ではリクエストの送信が成功したかどうかを、サーバがレスポンスを返すかどうかで判断することになります (アプリケーション層では、Nagleバッファ等の状態はわかりませんから)。ここで、「クライアントがレスボンスを読み取ろうとすることで、サーバへのリクエストの送信が遅延してしまう」ということがありうるでしょうか。……ありえないですよね。

TCP層で送信が遅延していれば (まだNagleバッファに入ったままのTCPセグメントがあれば)、アプリケーション層では「リクエストを送信した (write(2)を実行した) のにサーバからレスポンスが返ってこない (read(2)がブロックする)」ということになるでしょう。しかしこれはwrite-write-readの順に処理を行ったからではなく、もともとTCP層で2番目のwriteの送信が遅延しているからです。

次に、write-write-writeの場合。

TCP層では、writeが続いても、ACKが返ってこずセグメント長の総計が最大セグメント長 (MSS) に達するまでは、送信すべきセグメントがNagleバッファに追加され続けます。またそれ以前にアプリケーション層では、セグメントに分割する前のデータをOSのバッファに追加します。ですからアプリケーション層では、OSのバッファが一杯にならない限りいくらでもwrite(2)できます。要するにNagleアルゴリズムはほとんどまったく関係ありません

最後に、質問者さんが疑問に思っておられる点。

おっしゃる通り、ACKは常に返ってくるわけではありません (パケットロスもありますが、実装でもそうなっていません)。TCPではスライディングウィンドウのサイズ (MSSより大きい) の範囲内で複数のセグメントを送ることができ、そのどれかにACKが返ってきさえすれば、TCPシーケンス番号の整合性を確認することでそれ以前のセグメントが送信に成功したことがわかります。実際に遅延ACKも、ACKを毎回返さなくてもいいことが前提になっていますよね。

蛇足。もうひとつのウィキペディアの記事について。

ただし、最大セグメントサイズ以上のデータを受信したときは最低限2つ目のセグメントでACK応答を返さなければならない

RFC 1122, §4.2.3.2後半部の直訳のようですが、言っていることがよくわからないです。ここの趣旨は「MSSに達するサイズのセグメントが続けて送られてきたとき (つまりMSSの2倍かそれ以上のデータが送られたとき) は、セグメント2つ毎に1回ACKを返すべきだ (SHOULD)。」ということのように思います (... in a stream of full-sized segments there SHOULD be an ACK for at least every second segment.)。

つまり、理論的な限界 (全部MSSに達している場合) についてはこういうガイドラインを示すので、それ以外の場合は実装する人が各自工夫して下さいね、ということかと思います。現実の実装では、「セグメントサイズに関係なく2セグメントに1回はACKを返す」としているものもあるそうです (『マスタリングTCP/IP 入門編 第5版』p.244にそう書いてあった)。

投稿2017/01/01 12:31

ikedas

総合スコア4315

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

vc3000

2017/01/03 05:19

このSlashdotのコメントがおかしいのでしょうか。どうもJohn Nagle本人らしいのですが。 コメントは遅延ACKは悪いアイディアだと言っていますが、マスタリングTCP/IP入門編第5版p.245には優れた仕組みだと書かれていますね。 スティーブンス「UNIXネットワークプログラミング」第2版Vol.1 p.198には、ソフトウェアでの解決法として 1. 2回のwriteの呼び出しを1回のwritevに置き換える。これが最も望ましい解決方法である。 2. 2つのデータを1つのバッファにコピーしてwriteを1回だけ呼び出す 3. TCP_NODELAYを設定し、2回のwriteはそのままにしておく。これは最も望ましくない解決方法である。 と書かれていますね。
ikedas

2017/01/03 06:17

ネット上で見つかるバイオグラフィなどによれば、ご本人に間違いないと思います。ただ、アルゴリズムを考案したのは1980年代で、その後はネットワークの仕事から離れておられます。また、遅延ACKとの相性問題について当時は気づかなかったと、1999年ころ語ったとの第三者の証言があります。ご本人はけっこう気にしていらっしゃるみたいです。 回答は、書き方がちょっときつくて、全部おかしいみたいになっていますね。おかしいところは最後のユーザレベルの解決のところで「読み込みの前に書き込みのフラッシュを行うならば」としていることです。これはOSのバッファをフラッシュするということですから、Nagleバッファはフラッシュされない可能性があり、解決になりません。結局、Nagleアルゴリズムか遅延ACKかどちらかを無効にするのが有効な解決といえます。 また、投稿では「telnetは使われなくなったからインタラクティブな処理を考えなくていい」ととれることを言っていますが、時宜を得ない見解と思います。この問題はむしろHTTPパイプラインなどのデッドロック問題としてよく知られるようになったので、今日の問題です。 ご指摘の『UNIXネットワークプログラミング』について。3番目の解決法が望ましくないのは、「2回のwriteはそのまま」であるため「小さいパケット問題」が放置されるからですね。TCP_NODELAYが悪いわけではないです。実際、最近のネットワーク関係のコード (nginxなど) では、TCP_NODELAYをセットしたうえで、TCP_CORK/TCP_UNPUSHをセットしてなるべくセグメントサイズをMSSに近づけるとか、送信するデータをなるべくまとめる (ヘッダとボディを別々にwriteしない) といった戦略をとっているようです。 いずれにしても、Nagleアルゴリズムはパケットサイズ等と無関係に自動的に働いてしまう、ということが問題なので、必要に応じて無効にするというのは適切な対応かと思います (かつてアルゴリズムの改良 draft-minshall-nagle-01 も提案されましたが、RFC化に至りませんでした。無効にするほうが簡単ですよね)。 コメントにいろいろ書いてしまってすみません。回答に反映したほうがいいですかねえ。
vc3000

2017/01/03 12:11

とても勉強になりました。ありがとうございます。 回答に反映するかは、私はこのままで大丈夫です。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問