Linux Socket 编程调优与案例

14次阅读

so_rcvbuf/so_sndbuf 受 rmem_max/wmem_max 限制,et 模式需循环读至 eagain,emfile/enfile 需调 ulimit 或 file-max,epipe 需检查 errno 并主动 close。

Linux Socket 编程调优与案例

SO_RCVBUF 和 SO_SNDBUF 设置后没生效?

Linux 内核会限制 socket 缓冲区大小的上限,setsockopt 成功不代表最终值就是你设的。内核取的是「min(用户设置值, /proc/sys/net/core/rmem_max)」,写小了没效果,写大了被截断。

  • 先检查当前系统上限:cat /proc/sys/net/core/rmem_max/proc/sys/net/core/wmem_max
  • 若需调大,临时改法:sudo sysctl -w net.core.rmem_max=4194304(4MB);永久生效需写入 /etc/sysctl.conf
  • 调用 setsockopt 后,用 getsockopt 读回确认实际值,别只信返回值为 0
  • 注意:TCP 连接建立后,接收缓冲区大小还受 tcp_rmem 三元组动态影响,SO_RCVBUF 只设初始值

epoll_wait 返回就绪但 read 返回 EAGAIN?

这是边缘触发(ET)模式下的典型误用——没一次性读完所有可用数据,又没重新 epoll_ctl(EPOLL_CTL_MOD),导致后续就绪事件被丢弃。

  • ET 模式下,必须循环 read 直到返回 EAGAINEWOULDBLOCK,不能只读一次
  • 务必在 read 前把 errno 清零,否则旧错误码可能干扰判断
  • 如果用 recv(fd, buf, len, MSG_DONTWAIT) 替代 read,行为一致,但更明确表达非阻塞意图
  • LT 模式虽不强制循环读,但高吞吐场景下仍建议一次读尽,避免频繁唤醒

accept 返回 EMFILE 或 ENFILE 怎么办?

不是程序漏关 fd,而是进程或系统级文件描述符耗尽。前者是 ulimit -n 限制,后者是内核全局 file-max 限制。

  • 查当前限制:ulimit -n(进程级),cat /proc/sys/fs/file-max(系统级)
  • 临时提升进程上限:ulimit -n 65536,但必须在启动服务前执行,子进程继承
  • 检查 fd 泄漏:用 lsof -p <pid></pid> 看是否大量 socket 处于 close_wait 或未关闭
  • 注意 accept 返回的 socket 默认继承监听 socket 的 CLOEXEC 状态,若没显式设 FD_CLOEXEC,fork 后可能意外泄露

send 返回 EPIPE 却没触发 SIGPIPE?

默认会触发,但多数服务都调用了 signal(SIGPIPE, SIG_IGN) 屏蔽它——这时 send 直接返回 -1 并置 errno = EPIPE,而不是崩溃。

  • 不要依赖 SIGPIPE 终止连接逻辑,一律检查 send 返回值和 errno
  • EPIPE 意味着对端已关闭连接(如调用 close 或进程退出),此时应主动 close 本端 socket
  • 若用 send(……, MSG_NOSIGNAL),可临时禁用该次调用的 SIGPIPE,但无法绕过 EPIPE 错误本身
  • 注意:TCP 是全双工,EPIPE 只说明写端失效,读端可能还有残留数据,别急着删 fd

缓冲区大小、事件触发模式、fd 资源、信号处理——这四块看似独立,实则在高并发连接下会连锁反应。比如 SO_RCVBUF 设太小,导致应用层读不及时,socket 接收队列积压,最终触发 netstat 里大量 Recv-Q,进而拖慢 epoll_wait 效率,再加剧 fd 积压……调优得从链路看,不能只盯单点。

text=ZqhQzanResources