SO_LINGER 是控制 close() 后套接字等待未发送数据完成的选项,需在 close 前设置 linger=1 和非零超时,并配合 shutdown(SHUT_WR) 使用;设为 0 或 close 后设置均导致数据丢失。

SO_LINGER 是什么,为什么不能直接设成优雅关闭
SO_LINGER 本身不提供“优雅关闭”能力,它只控制 close() 或 shutdown() 后套接字的等待行为。设了 linger 且 l_onoff=1、l_linger>0,内核会在 close() 时阻塞,等未发送完的数据发完或超时;但若对端已断开(比如 RST),它仍会立即失败,不会重试或缓冲重发。所谓“清空残留发送缓冲”,实际依赖 TCP 自身的重传与 ACK 机制,SO_LINGER 只是决定你愿不愿意等这个过程完成。
正确设置 SO_LINGER 的三步操作
必须在 close() 前设置,且仅对已连接的 socket 有效(监听 socket 设置无效):
- 用
setsockopt()设置SO_LINGER,结构体linger中:l_onoff=1开启,l_linger设为期望等待秒数(如 5) - 调用
shutdown(sockfd, SHUT_WR)主动关闭写端,触发 FIN 发送并进入 FIN_WAIT_1 状态 - 再调用
close(sockfd)—— 此时内核才会按linger规则等待未 ACK 的数据发完
示例片段:
struct linger ling = {1, 5}; // 等 5 秒
setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
shutdown(sockfd, SHUT_WR); // 先发 FIN
close(sockfd); // 再 close,才真正触发 linger 等待
常见错误:close() 后才设 linger,或 linger=0
这两个操作等于主动放弃残留数据:
立即学习“C++免费学习笔记(深入)”;
-
l_onoff=1且l_linger=0:close()会立刻发送 RST,丢弃所有未确认数据,对端收到Connection reset by peer - 先
close()再setsockopt(... SO_LINGER ...):毫无意义,socket 已销毁,设置失败且 errno 通常是EBADF - 对 UDP socket 设置
SO_LINGER:被忽略(UDP 无连接状态),getsockopt()读回来仍是默认值
比 SO_LINGER 更靠谱的替代方案
真正想“清空发送缓冲并确保对端收到”,靠 SO_LINGER 不够稳,尤其在网络不稳定时。更可控的做法是:
- 应用层自己管理发送队列:调用
send()后检查返回值,记录未发送完的 buffer,配合select()或epoll_wait()等可写事件重试 - 发送关键数据后调用
shutdown(sockfd, SHUT_WR),再用recv()循环读直到返回 0(对端也关闭)或EAGAIN/EWOULDBLOCK - 需要强保证时,加应用层 ACK 协议——
SO_LINGER永远只是 TCP 层的尽力而为
注意:Linux 上 SO_LINGER 的等待逻辑受 /proc/sys/net/ipv4/tcp_fin_timeout 影响,超时值可能被系统参数截断,实测中设 30 秒 linger 可能只等 15 秒就强制断开。


















