第一百二十二章:选帆
问道期涉及内核源码:
一
林小源在网络之海的港口看到一艘正在调试的船。船上的帆匠正在仔细地调整帆的角度、绳索的松紧、船舵的灵敏度。
"你在做什么?"林小源问。
"调帆。"帆匠头也不回地说。
帆匠是一个精瘦的中年人,双手布满老茧,指甲缝里嵌着木屑。他的工作台上摆满了各种工具——每一件都刻着 socket 选项的名字:、、、。
"每艘船的帆都不一样。"帆匠终于转过身,"有些船要快——禁用 Nagle 算法,。有些船要稳——调大缓冲区,。有些船要灵活——允许多个进程绑定同一端口,。"
"这些选项都是用 设置的?"
"对。"帆匠拿起一把刻着 的工具,"比如这把——。服务器重启时,旧连接可能还在 TIME_WAIT 状态,端口被占用。设置了这个选项,新 socket 可以绑定到还在 TIME_WAIT 的地址上。"
二
"你听说过 Nagle 算法吗?"帆匠问。
林小源摇头。
帆匠从桌上拿起两块木板:"Nagle 算法会合并小数据包——如果你每次只发几个字节,Nagle 会把它们攒起来,等到凑够一个 MSS 或者收到前一个包的 ACK 再发送。"
"这不好吗?"
"减少网络开销。"帆匠把两块木板拼在一起,"但增加延迟。如果你在玩网络游戏,每次操作只发几十个字节,Nagle 会等——等几百毫秒才发出去。对交互式应用来说,这是灾难。"
"所以要 。"
"对。"帆匠把木板分开,"禁用 Nagle,数据立刻发送。SSH、游戏、实时应用——都需要 。但如果你是传文件,大数据流,Nagle 的合并反而有好处——减少小包数量,提高效率。"
"没有万能的配置。"
帆匠笑了:"这就是调优的艺术——没有银弹,只有权衡。每个选项都有适用场景,选错了比不选更糟。"
三
帆匠走到船边,指着船底的一个阀门:"这是 ——接收缓冲区大小。"
林小源蹲下身看那个阀门。阀门可以开大开小,控制着流入船舱的水量。
"缓冲区太小,数据包来了没地方放,就被丢掉。"帆匠说,"缓冲区太大,浪费内存——而且 TCP 的窗口大小受缓冲区影响,太大可能导致发送方发太多数据,反而造成拥塞。"
"那该多大?"
"看场景。"帆匠说,"高带宽长延迟的网络——比如跨洋传输——需要大缓冲区,否则带宽利用不起来。低延迟的局域网——小缓冲区就够了。内核有自动调整:net.ipv4.tcp_moderate_rcvbuf,让它自己调。"
"还有超时选项。"
"对。 和 。"帆匠拿起另一把工具,"设置接收和发送的超时时间——避免进程永远阻塞在 recv() 或 send() 上。"
林小源看着帆匠的工具箱,忽然明白了——socket 选项不是可有可无的参数,它们是调优的手段。选对了,船跑得快;选错了,船可能翻。
/*
* 常用的 socket 选项:
*
* SO_REUSEADDR:
* 允许重用本地地址
* 服务器重启时有用
*
* SO_REUSEPORT:
* 允许多个 socket 绑定同一端口
* 负载均衡
*
* SO_RCVBUF / SO_SNDBUF:
* 接收/发送缓冲区大小
* 影响性能
*
* SO_RCVTIMEO / SO_SNDTIMEO:
* 接收/发送超时
*
* TCP_NODELAY:
* 禁用 Nagle 算法
* 减少延迟
*
* TCP_KEEPIDLE / TCP_KEEPINTVL / TCP_KEEPCNT:
* TCP keepalive 参数
*/
printf("=== socket 选项 — 调优的手段 ===\n\n");
printf("常用的 socket 选项:\n\n");
printf("SO_REUSEADDR:\n");
printf(" 允许重用本地地址\n");
printf(" 服务器重启时有用\n");
printf(" setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))\n\n");
printf("SO_REUSEPORT:\n");
printf(" 允许多个 socket 绑定同一端口\n");
printf(" 负载均衡\n\n");
printf("SO_RCVBUF / SO_SNDBUF:\n");
printf(" 接收/发送缓冲区大小\n");
printf(" 影响吞吐量\n\n");
printf("SO_RCVTIMEO / SO_SNDTIMEO:\n");
printf(" 接收/发送超时\n");
printf(" 避免永久阻塞\n\n");
printf("--- TCP 选项 ---\n\n");
printf("TCP_NODELAY:\n");
printf(" 禁用 Nagle 算法\n");
printf(" 减少延迟\n");
printf(" 适合交互式应用\n\n");
printf("TCP_KEEPIDLE:\n");
printf(" 空闲多久后开始 keepalive\n\n");
printf("TCP_KEEPINTVL:\n");
printf(" keepalive 探测间隔\n\n");
printf("TCP_KEEPCNT:\n");
printf(" keepalive 探测次数\n\n");
printf("--- Nagle 算法 ---\n");
printf("Nagle 算法:\n");
printf(" 合并小数据包\n");
printf(" 减少网络开销\n");
printf(" 但增加延迟\n\n");
printf("TCP_NODELAY:\n");
printf(" 禁用 Nagle\n");
printf(" 立即发送\n\n");
printf("--- 查看 socket 选项 ---\n");
printf("getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, &len)\n");#include <stdio.h>
/*
* 常用的 socket 选项:
*
* SO_REUSEADDR:
* 允许重用本地地址
* 服务器重启时有用
*
* SO_REUSEPORT:
* 允许多个 socket 绑定同一端口
* 负载均衡
*
* SO_RCVBUF / SO_SNDBUF:
* 接收/发送缓冲区大小
* 影响性能
*
* SO_RCVTIMEO / SO_SNDTIMEO:
* 接收/发送超时
*
* TCP_NODELAY:
* 禁用 Nagle 算法
* 减少延迟
*
* TCP_KEEPIDLE / TCP_KEEPINTVL / TCP_KEEPCNT:
* TCP keepalive 参数
*/
int main() {
printf("=== socket 选项 — 调优的手段 ===\n\n");
printf("常用的 socket 选项:\n\n");
printf("SO_REUSEADDR:\n");
printf(" 允许重用本地地址\n");
printf(" 服务器重启时有用\n");
printf(" setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt))\n\n");
printf("SO_REUSEPORT:\n");
printf(" 允许多个 socket 绑定同一端口\n");
printf(" 负载均衡\n\n");
printf("SO_RCVBUF / SO_SNDBUF:\n");
printf(" 接收/发送缓冲区大小\n");
printf(" 影响吞吐量\n\n");
printf("SO_RCVTIMEO / SO_SNDTIMEO:\n");
printf(" 接收/发送超时\n");
printf(" 避免永久阻塞\n\n");
printf("--- TCP 选项 ---\n\n");
printf("TCP_NODELAY:\n");
printf(" 禁用 Nagle 算法\n");
printf(" 减少延迟\n");
printf(" 适合交互式应用\n\n");
printf("TCP_KEEPIDLE:\n");
printf(" 空闲多久后开始 keepalive\n\n");
printf("TCP_KEEPINTVL:\n");
printf(" keepalive 探测间隔\n\n");
printf("TCP_KEEPCNT:\n");
printf(" keepalive 探测次数\n\n");
printf("--- Nagle 算法 ---\n");
printf("Nagle 算法:\n");
printf(" 合并小数据包\n");
printf(" 减少网络开销\n");
printf(" 但增加延迟\n\n");
printf("TCP_NODELAY:\n");
printf(" 禁用 Nagle\n");
printf(" 立即发送\n\n");
printf("--- 查看 socket 选项 ---\n");
printf("getsockopt(fd, SOL_SOCKET, SO_RCVBUF, &val, &len)\n");
return 0;
}道藏笔记
内核启示
socket 选项允许调优 socket 的行为。
常用选项:
- — 重用本地地址
SO_RCVBUF/SO_SNDBUF— 缓冲区大小- — 禁用 Nagle 算法
- — keepalive 参数
Nagle 算法:
- 合并小数据包
- 减少网络开销
- 增加延迟
- 禁用
socket 选项是"选帆"——根据需求调整行为。
socket 选项之试
为了关闭 Nagle、降低小包交互延迟,本章使用的 TCP socket 选项是什么?