LoginSignup
8
7

More than 3 years have passed since last update.

Nagle algorythem / TCP_NODELAY をまとめる

Posted at

まだよくわかってないけど途中経過をメモっておく(ツッコミ歓迎)

Nagle algorythemとは

ssh/telnet接続中に、キーを一回押しただけのデータが 1 byteだと仮定して、このデータを送るとき、IPヘッダ+TCPヘッダ= 40 bytes を足して、計41bytes送らないといけない。非効率的。
キーを20回押して、せめて計60bytesになったら送ろうよ、というのがNagle algorythem. わざとdelayさせるのでこれがゲームだとすると、どのボタンを押してもしばらく送信されない状態になる。実際はMSSとかACKとか条件があるんだけどここでは省略。
とにかく Nagleが有効だと、送信が遅れる Nagleが無効ならすぐ送信する これを丸暗記する。

わかってないこと

世の中の通信のどれくらいが Nagle してるのか知りたい。僕は Nagle してるのか??? これがわからない。

どっちがどっちの状態だっけ

condition exaplain
TCP_NODELAY is enabled Nagle is disabled (no delay)
TCP_NODELAY is disabled Nagle is enabled (delay occurs)

TCP_NODELAYとは

一般的にwebサーバがポートを開くとき、OSレベルではソケットを開いてそれをポートにアタッチする的な感じになる(違うかも). javaでもphpでもnodejsでもOSでも大抵は実行時はC言語で書かれたコードが動いている。そのC言語でsocketを開くコードがこれ

sock = socket(AF_INET, SOCK_STREAM, 0);

apacheでもnginxでもなんでも、僕らが知らないところでこのコードが動いている(たぶん)。
このsocketは、デフォルトで TCP_NODELAY = False つまり nagleが有効なモード(=送信が遅い)である。C言語はデフォルトでスピードよりNWの効率的な利用を目指しているといっていいのだろうか。

作ったsocketに TCP_NODELAY をわざわざ適用することで、Nagleはoffになる = すぐ送信するモードになる

setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, len)

C言語を書くプログラマは、ここでそのアプリケーションがゲーム向きなのか、ファイル転送向きなのか選ぶ(パケットは小さいか/リアルタイム転送が必要かどうかなど)。このオプションはjavaでもOSでも、そっち側からコントロールできるオプションがあったりする。紛らわしいのはそれぞれで名前が違って、機能もちょっと違うところだ。ぐぐってると、Cの話ばかり出てくるので混乱する。

default settings

果たして何も考えずに各OS, middlewareからtcpパケットを送ったら、それはnagleなのかどうなのか? コレが知りたいんだけど、よくわからない。

ここから恐らく永遠に未完成な記事になる

platform default state note
linux
apache
ssh

Linux

かつては sysctlの tcp_low_latency がそれっぽい何かだったようだがKernel 4.? からなくなった。

tcp_low_latency (Boolean; default: disabled; since Linux 2.4.21/2.6;
obsolete since Linux 4.14)
If enabled, the TCP stack makes decisions that prefer lower
latency as opposed to higher throughput. It this option is
disabled, then higher throughput is preferred. An example of
an application where this default should be changed would be a
Beowulf compute cluster. Since Linux 4.14, this file still
exists, but its value is ignored.
https://man7.org/linux/man-pages/man7/tcp.7.html

合わせて読みたい

https://m.blog.naver.com/PostView.nhn?blogId=bestdriver94&logNo=221142341347

apache

よくわからない。そもそも各middlewareにデフォルト値があると思ってるのが間違ってるのかもしれない。
見つかったのは Apache HttpClient というもののマニュアル。Apache HttpClientは、javaでcurlが叩けるようなライブラリなのかもしれない。

CoreConnectionPNames.TCP_NODELAY='http.tcp.nodelay'
https://hc.apache.org/httpcomponents-client-4.2.x/tutorial/html/connmgmt.html

http requestを送るときはtcpパケット送るので、そのときに TCP_NODELAY かどうかを選べるんだろう。これを変えると、その下で動いてるC-langが、オプションを変えてくれるんだろう。

謎は謎のまま。

ssh

よくわからんけど openssh/openssh-portable のソースをgrepしてみた。
https://github.com/openssh/openssh-portable/search?q=setsockopt

set_nodelay という関数ないでは TCP_NODELAY をしている。


set_nodelay(int fd)
{
        ...
    if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof opt) == -1)

https://github.com/openssh/openssh-portable/blob/816036f142ecd284c12bb3685ae316a68d2ef190/misc.c#L153

set_nodelay は以下の4ファイルで呼ばれている。

  • misc.h
  • channels.c
  • misc.c
  • packet.c

https://github.com/openssh/openssh-portable/search?q=set_nodelay

例えばここでは、 PORT_STREAMLOCAL かどうかで nodelayを切り替えている。

    if (c->host_port != PORT_STREAMLOCAL)
        set_nodelay(newsock);

https://github.com/openssh/openssh-portable/blob/816036f142ecd284c12bb3685ae316a68d2ef190/channels.c#L1794

この意味はわからないけど、状況によって Nagle を切り替えて使っているということはわかる。

いま時点の結論(おおざっぱ)

この記事の結論として、僕らは Nagle をTCP通信ひとつずつに対して、随時 on/off 切り替えながら使っているのかもしれない。

授業料払うから、新宿あたりで誰かビール飲みながら教えてくれないかしらん。

TCP_CORKとは

TCP_NODELAYのちょっと違う版ぽい。Cでsocketのオプション入れる時に、TCP_NODELAYの代わりにいれる。

    setsockopt(sock, IPPROTO_TCP, TCP_CORK, &val, sizeof(val));

この記事が素敵ぽいのであとで試してみる。
https://qiita.com/hana_shin/items/9b4f4c5e2c8e740c61bb

8
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
7