系统文件
/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 或者缓冲区积累够大时再发。
- 如果要发送的数据很大(≥ MSS,最大报文段长度),直接发。
- 如果应用要发小数据:
- 有未确认的包 → 暂时把新数据放在缓冲区里,不发。
- 收到了 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 | cat /proc/sys/net/ipv4/tcp_keepalive_time |
应用场景
- 长连接 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 来发现死连接
- RST 只在一些场景出现:
场景 | 服务端能否立即发现 | 是否需要 Keepalive |
---|---|---|
客户端正常 close() | 是 | 否 |
客户端异常掉线(断电、拔网线) | 否 | 是 |
客户端本地 abort() 或 write 已关闭套接字 | 是(会收到 RST) | 否 |
所以 SO_KEEPALIVE 主要用于发现客户端异常掉线。没有它,长连接可能永远挂在 ESTABLISHED,资源泄漏。