SACK / ACK

# 是否允许 TCP 发送 "duplicate" SACKs
net.ipv4.tcp_dsack = 1
net.ipv4.tcp_sack = 1

# 限制每秒发送 Challenge ACK 个数.
net.ipv4.tcp_challenge_ack_limit = 1000

# 限制响应现有传入 TCP 数据包但由于以下原因而无效的重复 ACK 的最大速率
# - out-of-window sequence numbe
# - out-of-window acknowledgment number
# - PAWS (Protection Against Wrapped Sequence numbers) check failure
# 单位为 ms. 换句话说, 这限制了发送重复 ack 的最小时间间隔.
net.ipv4.tcp_invalid_ratelimit = 500

window/buffer

# socket 读写缓冲区相关配置. 这个是所有协议中 每个 socket 的默认以及最大大小. 单位字节.
# 注意, 只有 default 值可以被覆盖, max 的值是硬性的.
net.core.rmem_default = 212992
net.core.rmem_max = 16777216
net.core.wmem_default = 212992
net.core.wmem_max = 16777216


# 是否开启自动调优接收缓冲区大小
net.ipv4.tcp_moderate_rcvbuf = 1

# [low, pressure, high] 系统总 TCP 分配内存. 当内存超过 pressure 水位时, 就是触发自动调优.
# pressure 在内存降到 low 的时候就会退出
# 注意, 这些单位是 system page (通常是 4KB/页)
net.ipv4.tcp_mem = 384462	512617	768924


# 这些是用于每一个 TCP 连接的. 这个单位是 字节.
# 注意, min 并不是用于 SO_RCVBUF 参数绑定声明的.
# 这里的 default 的值, 会覆盖 net.core.rmem_default / net.core.wmem_default 的值. 如果想增大 receive buffer 的大小, 可以增加 tcp_rmem 的 default 的值大小. 注意, 为了开启大的 TCP windows, net.ipv4.tcp_window_scaling 必须要开启(默认是开启的)
# 注意, max 这个值并不会覆盖 net.core.rmem_max / net.core.wmem_max 的值.
# 自动调优时使用的值, 分别为 min, default, max
net.ipv4.tcp_rmem = 4096	87380	16777216
net.ipv4.tcp_wmem = 4096	65536	16777216

# 是否开启 window 缩放
net.ipv4.tcp_window_scaling = 1

# man tcp , 然后搜索 adv
# 参考  https://unix.stackexchange.com/questions/94770/what-does-net-ipv4-tcp-app-win-do
net.ipv4.tcp_adv_win_scale = 2

# 保留多少字节 tcp window 用于缓冲开销. 
# 保留 window 中的 max(window/2^tcp_app_win, mss) 字节用于应用程序 buffer. 0 表示不保留.
net.ipv4.tcp_app_win = 31

# 每个 TCP Socket 的 TCP Small Queue 的限制.
## 它限制了在 qdisc 或 device 中的字节数, 以减少人为 RTT/cwnd 和 减少 bufferbloat
## qdisc 是一个调度器. 每个输出设备都需要一种调度, 默认的调度器是 FIFO.
## bufferbloat : buffer 溢出, 从而导致高延时.
net.ipv4.tcp_limit_output_bytes = 262144

# 跟踪最小 RTT 的最小窗口过虑器中的窗口长度
net.ipv4.tcp_min_rtt_wlen = 300

# 由于这个选项, TCP socket 可以控制 write queue 中的总未发送字节数.
net.ipv4.tcp_notsent_lowat = -1

# 如果设置, 假设没有收到 window 缩放选项意味着远程 TCP 已损坏, 并将 window 缩放视为 signed 的数量
# 如果没有设置, 即使我们没有收到 window 缩放选项, 也假设远程 TCP 没损坏
net.ipv4.tcp_workaround_signed_windows = 0

如果 tcp_adv_win_scale > 0 , 则公式为 bytes / (2^tcp_adv_win_scale) . 如果 tcp_adv_win_scale <=0 ,则公式为 bytes - bytes/2^(-tcp_adv_win_scale)

Socket 的 receive buffer 是由应用程序与内核共享的. TCP 维护部分缓冲区作为 TCP window , 这是作为通告另一端的 window 大小. 剩下的部分用于应用程序的缓冲区, 用于隔离调度与应用程序延时. tcp_adv_win_scale 默认为 2, 表示用于应用程序的缓冲区是总数的 14.

那理论上, 通告的 win 大小为 (tcp_adv_win_scale > 0) $$ 通告 win = bytes * (1-\frac{1}{2^{tcp_adv_win_scale}}) $$

我的理解是
|                        A                             | (A 是 receive buffer 总大小)
|  adv win               |    application              | (由 `tcp_adv_win_scale` 控制比例)
|  adv win               | application |`tcp_app_win`  | (由 `tcp_app_win` 控制app的保留大小)

keepalive

net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_time = 7200

TIME WAIT

# 系统允许的最大的 TIME_WAIT 状态的 socket 数量
net.ipv4.tcp_max_tw_buckets = 5000

# 是否快速回收 TIME WAIT socket. 通常不要启用. 
net.ipv4.tcp_tw_recycle = 0

# 是否重用 TIME WAIT socket. 这只是对一条新的 outgoing 的连接才考虑复用. 对于 incoming 的连接没效果.
net.ipv4.tcp_tw_reuse = 0

# TIME WAIT 等待多久后就完全关闭. 单位秒
net.ipv4.tcp_fin_timeout = 60

# 当禁用时, 如果在 TIME_WAIT 状态时收到一个 RST, 则立即关闭这个 socket, 而不用等 TIME_WAIT 结束.
net.ipv4.tcp_rfc1337 = 0

SYN

net.ipv4.tcp_max_syn_backlog = 1024
net.ipv4.tcp_syn_retries = 6
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syncookies = 1

RST

# 当系统处理不过来时, 返回给客户端 RST. (没特殊情况的话, 不要开启)
# 因为返回 RST, 客户端可能认为服务器不可达, 导致不会重试. 而不是认为服务器繁忙.
net.ipv4.tcp_abort_on_overflow = 0

PMTU

net.ipv4.tcp_mtu_probing = 0

# 如果上面 MTU probing 的参数开启, 则这个值就是初始的 MSS 值
net.ipv4.tcp_base_mss = 1024

# 控制多久开始重新探测 TCP Packetization-Layer Path MTU Discovery. 单位秒
net.ipv4.tcp_probe_interval = 600

# 控制当进行 TCP Packetization-Layer Path MTU Discovery 停止条件. 即搜索范围的宽度. 以字节为单位.
## 
net.ipv4.tcp_probe_threshold = 8

TCP metrics

# 是否关闭 tcp 指标收集. 0: 表示收集. 1: 不收集.
# 默认为 0, 收集.
net.ipv4.tcp_no_metrics_save = 0

拥塞相关

拥塞窗口 cwnd 衡量的是发送方的速率大小

通告窗口(接收窗口) rwnd 衡量的是接收方处理数据的能力大小.

net.ipv4.tcp_allowed_congestion_control = cubic reno
net.ipv4.tcp_available_congestion_control = cubic reno
net.ipv4.tcp_congestion_control = cubic

# 一种拥塞算法. 它是基于 SACK 的. 只有在 SACK 开启的前提下才行.
net.ipv4.tcp_fack = 1

# ECN : Explicit Congestion Notification . 显式拥塞通知. 只有两端都支持才起作用.
# 0 为禁用. 1 当接收ECN 时开启, 并且尝试也发送一个 ECN 出去. 2: 与 1 相似, 但不发送一个 ECN 出去.
net.ipv4.tcp_ecn = 2

# 当内核检测到一个错误有 ECN 行为, 则返回到非 ECN. 当上面的 tcp_ecn 开启的时候才起作用.
net.ipv4.tcp_ecn_fallback = 1

# 当前速率为 (current_rate = cwnd * mss / srtt).
## 当 TCP 处于拥塞避免阶段是, tcp_pacing_ca_ratio 用于保守探测更大的吞吐量
net.ipv4.tcp_pacing_ca_ratio = 120
## 当 TCP 处于慢启动, tcp_pacing_ss_ratio 用于探测更大的速率, 假设 cwnd 可以每隔一个 RTT 加倍.
net.ipv4.tcp_pacing_ss_ratio = 200


# 当开启, 则提供 RFC 2861 的行为以及在一个 idle 时间(即一个 RTO, 重传超时)后拥塞窗口超时.
## 当禁用时, 拥塞窗口在一个 idle 时间后不会超时
net.ipv4.tcp_slow_start_after_idle = 1

timestamp

# tcp 选项中的 timestamp 是否开启.(通常建议开启)
net.ipv4.tcp_timestamps = 1

CORK/NODELAY

# 内核自动尝试尽可能地合并 small write, 以减少发送包的总数.
# 也可以在应用程序中, 显式设置 TCP_CORK
net.ipv4.tcp_autocorking = 1

# TCP_NODELAY 会被 TCP_CORK 覆盖. 
# 设置 TCP_NODELAY 会显式地 flush write 的数据, 尽管 TCP_CORK 已设置.

超时/重传/恢复

net.ipv4.tcp_early_retrans = 3

# Forward RTO-Recovery (F-RTO)
# 它是一个加强版的 TCP 重传超时的 recovery 算法. 0 表示禁用. 非 0 表示开启. 它是 sender only, 不要求对方也支持.
net.ipv4.tcp_frto = 2

# 实验性的丢失恢复特性.
## RACK: 0x1 enables the RACK loss detection for fast detection of lost
##         retransmissions and tail drops. It also subsumes and disables
##         RFC6675 recovery for SACK connections.
## RACK: 0x2 makes RACK's reordering window static (min_rtt/4).
## RACK: 0x4 disables RACK's DUPACK threshold heuristic
net.ipv4.tcp_recovery = 1

# TCP 在一个已正常建立的连接中尝试重传的次数. 参考 https://emacsist.github.io/2019/07/11/tcp-ip协议笔记/
## TCP 拥有两个阈值来决定如何重传同一个报文段.
## R1 表示 TCP 在向 IP 传递消极建议(如重新评估当前 IP 路径)前, 愿意尝试重传的次烽(或等待时间).
## R2 (大于 R1)指示 TCP 应放弃当前连接的时机.
## R1 和 R2 应分别至少设为 3 次重传和 100 秒.
## 对数据段, 分别对应系统配置变量 net.ipv4.tcp_retries1 (默认为 3), 以及 net.ipv4.tcp_retries2(默认为 15, 对应约 13 ~ 30 分钟, 根据具体连接的 RTO 而定) . 值为重传次数, 而不是时间.

net.ipv4.tcp_retries1 = 3
net.ipv4.tcp_retries2 = 15

# 在重传中, 尝试发送完整大小的数据包
net.ipv4.tcp_retrans_collapse = 1

TSO

# 每个 TSO frame 的最小 segment 数.
## TSO(TCP Segment Offload)是一种利用网卡的少量处理能力,降低CPU发送数据包负载的技术,需要网卡硬件及驱动的支持
## 可以用命令 ethtool -k eth0 来查看是否支持 TSO 功能.
net.ipv4.tcp_min_tso_segs = 2

# 允许控制 拥塞窗口的百分比可以被一个 TSO frame 消费
net.ipv4.tcp_tso_win_divisor = 3

fast open

# 是否在 SYN 包中开启 TCP Fast Open(RFC 7413) 来发送和接收数据.
# The values (bitmap) are
# 0x1: (client) enables sending data in the opening SYN on the client.
# 0x2: (server) enables the server support, i.e., allowing data in
#     a SYN packet to be accepted and passed to the
#     application before 3-way handshake finishes.
# 0x4: (client) send data in the opening SYN regardless of cookie
#     availability and without a cookie option.
# 0x200: (server) accept data-in-SYN w/o any cookie option present.
# 0x400: (server) enable all listeners to support Fast Open by
#     default without explicit TCP_FASTOPEN socket option.
net.ipv4.tcp_fastopen = 1
net.ipv4.tcp_fastopen_key = e3fb03ef-c56d92c4-01a94033-6ca6fd72

未分类

net.ipv4.tcp_fwmark_accept = 0
net.ipv4.tcp_thin_dupack = 0
net.ipv4.tcp_thin_linear_timeouts = 0

orphan

# 没附加到任何用户的文件句柄(即孤儿 socket), 只被系统持有的最大 TCP socket 数
## 只是为了防止简单的 Dos 攻击. 不要依赖它或人为地调低这个值.
## 注意, 每个 orphan 会吃掉约 64K 的物理内存.
net.ipv4.tcp_max_orphans = 65536

# 尝试探测一条连接的另一端是否已关闭的最大尝试次数.
net.ipv4.tcp_orphan_retries = 0

reorder 重排序

# 初始化一条 TCP stream 的重排序级别. 即无序包的容错率.
net.ipv4.tcp_reordering = 3
# 最大重排序级别
net.ipv4.tcp_max_reordering = 300

latency

# 内核 TCP 栈决策倾向. 默认是吞吐优先. 如果开启这个, 则表示低延时优先.
# 注, 最新版的这个选项已经被移除(2017 Jul 30). https://github.com/torvalds/linux/commit/b6690b14386698ce2c19309abad3f17656bdfaea?diff=split
net.ipv4.tcp_low_latency = 0

urgent pinter 紧急指针

# 极少使用遇到, 还不是很理解~
net.ipv4.tcp_stdurg = 0

Linux 中的 Socket 相关参数

TCP_CONGESTION

string 类型. 指定socket 的拥塞控制算法.

非特权进程, 只允许在 tcp_allowed_congestion_control 中允许的算法.

特权进程, 则可以在 tcp_available_congestion_control 中任一选择.

TCP_CORK

如果设置, 则不发送部分帧.(即小于 MSS 的帧). 对于调用 sendfile 或吞吐量优化用处比较大.

如果设置的话, 则达到以下两个条件之一才会发送帧:

  • 达到一个 MSS 大小的帧
  • 200 ms

2.5.71 及之后, 才可以与 TCP_NODELAY 结合一起使用.

TCP_DEFER_ACCEPT

允许一个监听器只在数据到达的时候才唤醒.

TCP_INFO

用于收集该 socket 的信息.内核会返回一个 tcp_info 的结构体

TCP_KEEPCNT

在销毁该连接之前允许的最大 keepalive 探测数.

TCP_KEEPIDLE

单位秒. 在发送 keepalive 探测之前空闲的时间.

TCP_KEEPINTVL

单位秒. 每次发送 keepalive 探测间隔时间

TCP_LINGER2

orphanedFIN_WAIT2 状态的存活时间. 用于覆盖系统级别设置的 tcp_fin_timeout

TCP_MAXSEG

设置发送数据的 MSS. 如果超出 MTU 的话, 是则是无效的.

TCP_NODELAY

如果为 true 的话, 则禁用 Nagle 算法. 这意味着 segment 会尽快发送, 即使它只有一点数据.

如果为 false 的话, 则缓存一些数据, 以减少发送小数据的频率(会导致网络的使用率低).

它会被 TCP_CORK 覆盖. 但是, 显式设置该参数, 会刷新发送的数据, 即使 TCP_CORK 有设置.

TCP_QUICKACK

是否开启快速 ACK. 快速 ACK 模式中, ack 会被立即发送, 而不是延迟.

TCP_SYNCNT

设置 SNC 重传次数. 不能超过 255.

TCP_USER_TIMEOUT

>=0 的值. 当大于 0 时, 指定连接中已发送但还没 ack 的最大的时间(单位 ms) , 超过的话则关闭连接.

为 0 时, 则使用系统默认.

TCP_WINDOW_CLAMP

绑定通告的 window 大小为该值. 内核会在它与 SOCK_MIN_RCVBUF/2 之间选一个最大值.

SOCK_MIN_RCVBUF 是 256 字节

if ((val * 2) < SOCK_MIN_RCVBUF)
        sk->sk_rcvbuf = SOCK_MIN_RCVBUF;

网络命令

ss

http://man7.org/linux/man-pages/man8/ss.8.html

Recv-Q : Established 状态的表示用户应用还没有复制的数据
				 Listening 状态的表示当前 syn backlog 
Send-Q : Established 状态的表示还没有被对方 ACK 的字节数
         Listening 状态的表示最大的 syn backlog 数.(例如 nginx 中的 listen backlog=65535). 这里就会显示 65535

常用参数

  • -a : 列出所有监听和非监听(对于 TCP 则是 ESTABLISHED 状态) 的所有 socket
  • -l : 只列出监听状态的 socket(默认情况下不显示这种状态)
  • -o : 表示 timer 信息. 格式为 timer:(<timer_name>,<expire_time>,<retrans>)
    • timer_name
    • on : means one of these timers: TCP retrans timer, TCP early retrans timer and tail loss probe timer
    • keepalive
    • timewait
    • persist : zero window probe timer
    • unknown
    • expire_time : 还有久过期
    • retrans : 重传次数
  • -e : 显示详细socket信息. 格式为 uid:<uid_number> ino:<inode_number> sk:<cookie>
    • uid : 表示该 socket 属于哪个用户. 可以通过命令, 根据 uid 显示用户名 id -nu 用户ID
    • inode : socket 的 inode 数
    • cookie : socket 的 uuid
  • -m : 显示 socket 内存使用. 输出的格式为 skmem:(r<rmem_alloc>,rb<rcv_buf>,t<wmem_alloc>,tb<snd_buf>,f<fwd_alloc>,w<wmem_queued>,o<opt_mem>,bl<back_log>)
    • rmem_alloc : 已分配的 receive 内存
    • rcv_buf : 总可的分配 receive 内存
    • wmem_alloc : 已分配的 send 内存 (已发送数据到 layer 3)
    • snd_buf : 总的可分配 send 内存
    • fwd_alloc : socket 用于内存分配的 cache, 但还未用于 receive 或 send . 如果需要内存, 则优先从这里分配.
    • wmem_queued : 已分配的用于 send 的内存队列大小(还没有发送到 layer 3)
    • ropt_mem : 用于存储 socket 选项的内存.
    • back_log : 用于 socket backlog 队列内存.
  • -p : 显示进程使用中的 socket
  • -i : 显示 TCP 内部信息. 格式例子: cubic wscale:7,9 rto:220 rtt:16.331/16.891 ato:68 mss:1448 cwnd:10 ssthresh:7 bytes_acked:7419 bytes_received:67286 segs_out:75 segs_in:125 send 7.1Mbps lastsnd:3636 lastrcv:3640 lastack:3636 pacing_rate 8.5Mbps rcv_rtt:36 rcv_space:28960
    • cubic : 表示拥塞算法
    • wscale : 绽放窗口因子. 分别是 snd_wscale,rcv_wscale
    • rto : 重传超时值, 单位 ms
    • rtt : 单位 ms
    • ato : ack time out
    • mss : 最大 segment 大小, 字节
    • cwnd : 拥塞窗口大小
    • ssthresh : 慢启动阈值
    • lastsnd : 最后一次发送 packet 以来的时间, 单位 ms
    • lastrcv : 最后一次接收 packet 以来的时间, 单位 ms
    • lastack : 最后一次 ack 以来的时间, 单位 ms
    • pacing_rate : 发送速率
    • rcv_rtt : the time to receive one full window . https://www.spinics.net/lists/netdev/msg295886.html
    • rcv_space : 一个用于帮助系统自动调优 rec buf 的变量值
  • -s : 显示统计信息
  • -4 : 只显示 ipv4
  • -t : 显示 TCP socket