Skip to content

第一百二十二章:选帆

问道期

涉及内核源码:

林小源在网络之海的港口看到一艘正在调试的船。船上的帆匠正在仔细地调整帆的角度、绳索的松紧、船舵的灵敏度。

"你在做什么?"林小源问。

"调帆。"帆匠头也不回地说。

帆匠是一个精瘦的中年人,双手布满老茧,指甲缝里嵌着木屑。他的工作台上摆满了各种工具——每一件都刻着 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 选项不是可有可无的参数,它们是调优的手段。选对了,船跑得快;选错了,船可能翻。

c
/*
 * 常用的 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");

道藏笔记

内核启示

socket 选项允许调优 socket 的行为。

常用选项:

  • — 重用本地地址
  • SO_RCVBUF/SO_SNDBUF — 缓冲区大小
  • — 禁用 Nagle 算法
  • — keepalive 参数

Nagle 算法:

  • 合并小数据包
  • 减少网络开销
  • 增加延迟
  • 禁用

socket 选项是"选帆"——根据需求调整行为。


破关试炼

socket 选项之试

为了关闭 Nagle、降低小包交互延迟,本章使用的 TCP socket 选项是什么?

答对后才能继续滑动和进入下一章。

以修仙之名,悟内核之道