Nagle algorythem / TCP_NODELAY をまとめる

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

# 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を開くコードがこれ

```c
sock = socket(AF_INET, SOCK_STREAM, 0);
```

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

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

```c
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が叩けるようなライブラリなのかもしれない。](https://www.codeflow.site/ja/article/webservices__jax-rs__restful-java-client-with-apache-httpclient)

> 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 をしている。

```c

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を切り替えている。

```c
	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の代わりにいれる。

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

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