0%

网络编程

系统文件

/etc/services: 端口列表
/etc/protocols: 协议列表

socket 参数

SO_REUSEADDR

主要功能:允许端口重用。客户端一般不用,只在服务端常见。
更准确地说,它有几个具体效果(以 TCP 为例):

TIME_WAIT 状态下可重绑定

正常情况下,当服务端关闭套接字,端口进入 TIME_WAIT 状态(持续 ~1-4 分钟)

如果不设置 SO_REUSEADDR,你必须等 TIME_WAIT 结束才能再次 bind() 相同端口

设置后,可以立即重新 bind()

多个套接字绑定到同一个端口(但 IP 必须不同)

常见于多播、广播,或者一台机器上多个不同地址绑定同一个端口

Linux 特性:允许 相同 IP 和端口 多个套接字同时监听(结合 SO_REUSEPORT 使用更常见,nginx 就依赖这个做负载均衡)。

TIME_WAIT

TIME_WAIT 只会出现在主动关闭连接的一方。

一般情况:客户端主动关闭 → 客户端 TIME_WAIT,服务端不需要考虑

特殊情况:如果服务端主动关闭 listen_fd(例如服务进程退出后重启),就会产生 TIME_WAIT,需要优化手段。

SO_REUSEPORT

多进程/线程共享端口(同一IP+端口),避免 TIME_WAIT 堆积影响单个进程。前提是都设置了 SO_REUSEPORT)。

内核会 均匀分发新连接 到这些套接字(负载均衡)。

常见用途:

  • 高性能网络服务(nginx、redis、haproxy 等)
  • 利用多进程/多线程提升网络吞吐量

与 SO_REUSEADDR 的区别

选项 功能 使用场景
SO_REUSEADDR 允许端口在 TIME_WAIT 状态下重用 服务端重启,避免 “Address already in use”
SO_REUSEPORT 允许同一端口被多个进程/线程同时绑定,内核负载均衡 高性能多进程网络服务
  • SO_REUSEADDR → 解决 TIME_WAIT 问题
  • SO_REUSEPORT → 解决 多进程监听同一端口的负载均衡

TCP_NODELAY

用于关闭 Nagle 算法。

  • 背景:Nagle 算法

    • Nagle 算法的作用:

      把很多小的 TCP 包合并成一个大包再发送,以减少网络中小包的数量,提高效率。

      规则:如果前一个包还没被确认(ACK),新的小包不要立刻发送,而是先缓存,等到收到 ACK 或者缓冲区积累够大时再发。

      1. 如果要发送的数据很大(≥ MSS,最大报文段长度),直接发。
      2. 如果应用要发小数据:
        • 有未确认的包 → 暂时把新数据放在缓冲区里,不发。
        • 收到了 ACK → 把缓存的数据打包一起发送。
    • 代价:

      会造成 延迟(例如即时通信、RPC 请求这种场景,一次 send() 可能会等下一个小包一起发)。

✅ 建议使用的场景:

  • RPC 框架
  • 即时通讯(IM)
  • 游戏服务器(低延迟要求)
  • 高频短小消息的交互

❌ 不建议使用的场景:

  • 大文件传输(比如 HTTP 下载)
  • 视频流、音频流(关闭 Nagle 反而降低效率)

SO_KEEPALIVE

开启 SO_KEEPALIVE 后,内核在后台做三件事(以 Linux 默认值为例,可能因系统不同而不同):

  • tcp_keepalive_time(默认 7200s = 2小时)
    如果一个连接在 tcp_keepalive_time 时间内都没有任何数据往来,内核会开始发送探测包。

  • tcp_keepalive_intvl(默认 75s)
    探测包之间的间隔。

  • tcp_keepalive_probes(默认 9次)
    如果连续 tcp_keepalive_probes 次探测都没有回应,内核会认为连接已经断开,并通知应用层 read()/recv() 返回 0 或 ECONNRESET。

可以通过 /proc/sys/net/ipv4/tcp_keepalive_* 修改这些参数:

1
2
3
4
5
6
cat /proc/sys/net/ipv4/tcp_keepalive_time
cat /proc/sys/net/ipv4/tcp_keepalive_intvl
cat /proc/sys/net/ipv4/tcp_keepalive_probes

# 修改方式
sysctl -w net.ipv4.tcp_keepalive_time=600

应用场景

  • 长连接 RPC、心跳检测:避免死连接无限挂着。
  • 服务端防止资源泄漏:客户端异常掉线时,服务端能最终释放连接。
  • 中间设备 NAT/防火墙:如果链路长时间无流量,可能被踢掉,Keepalive能保持活动状态(但注意默认周期太长,往往要调低)。

RESET vs KeepAlive

  • 客户端正常断开

    • 客户端调用 close() 或 shutdown()

    • 会向服务端发送 FIN,服务端的 read() 返回 0

    • 这是“主动优雅关闭”,服务端能马上感知

      ✅ 这种情况下不需要 SO_KEEPALIVE

  • 客户端异常掉线(断电、拔网线、进程挂掉)

    • TCP 不能立即知道对方掉线

    • 为什么?

      • TCP 是端到端协议,除非收到 RST 或 FIN,否则内核认为连接仍然存在
      • 异常掉线不会发送 FIN
      • 网络异常(如网线拔掉)也不会发送 RST
      • 这时服务端的 read() / write() 不会马上报错
      • 写时可能阻塞
      • 读时可能一直阻塞

      ✅ 所以需要 SO_KEEPALIVE 来让内核周期性探测,最终发现对方掉线

  • 客户端发送 RST 的情况

    • RST 只在一些场景出现:
      • 客户端向已经关闭的套接字写数据
      • 本地进程调用 abort()
    • 但 异常掉线(掉电、断网、进程被 kill -9)不会发 RST
    • 因此 服务端不能依赖 RST 来发现死连接
场景 服务端能否立即发现 是否需要 Keepalive
客户端正常 close()
客户端异常掉线(断电、拔网线)
客户端本地 abort() 或 write 已关闭套接字 是(会收到 RST)

所以 SO_KEEPALIVE 主要用于发现客户端异常掉线。没有它,长连接可能永远挂在 ESTABLISHED,资源泄漏。

参考

端口0的作用