60 秒回答模板

如果某个客户端应用层不读数据,它的接收缓冲区会满,TCP 窗口变小甚至变成 0。服务端继续 send 时,内核发送缓冲区也会积压。阻塞 socket 下,send 可能阻塞,单进程模型会被这个慢客户端拖住,影响其他连接;非阻塞 socket 下,send 会返回 EAGAIN 或只写入部分数据,服务端需要保存未发送数据,等可写事件再继续,同时设置超时、限流和断开慢连接策略。

考点 TCP 流控
难度 真实面经题
回答目标 讲清方法、取舍和追问

深入解析

01

先看 TCP 缓冲区

客户端不调用 recv,不代表网络立刻断开,而是客户端内核接收缓冲区逐渐填满。接收窗口变小后,服务端可发送的数据量会被 TCP 流控限制。

02

阻塞模式会拖住进程

单进程阻塞模型中,如果对这个客户端 send 时卡住,进程就无法继续处理其他客户端的读写事件,慢连接会放大成整体服务不可用。

03

非阻塞模式返回可恢复错误

非阻塞 socket 发送不了时通常返回 EAGAIN/EWOULDBLOCK,或者只写入部分字节。应用层要记录剩余数据,而不是认为连接必然失败。

04

事件循环处理可写

正确做法是用 select、poll、epoll 等机制监听可写事件,缓冲待发送数据,在 socket 可写时继续 flush,同时避免无限缓存。

05

慢客户端需要背压

工程上要设置每连接输出缓冲区上限、写超时、心跳检测、丢弃低优先级消息或主动断开,防止一个客户端消耗过多内存和调度资源。

易错点

  • 不要以为客户端不 recv 服务端会立即报错,TCP 先通过缓冲和窗口控制表现出来。
  • 不要在单进程服务里使用无保护的阻塞写。
  • 不要忽略部分写,非阻塞 send 成功也可能只写出一部分数据。

面试官追问

非阻塞 send 返回 EAGAIN 后怎么办?

保存未发送数据,注册可写事件,等 socket 可写后继续发送,并限制缓冲区大小。

为什么会出现零窗口?

接收端缓冲区满且应用不读取时,会通告很小或为 0 的窗口,发送端只能等待窗口更新。

如何防止慢客户端拖垮服务?

设置写缓冲上限、写超时、限速、消息降级和主动断开策略,保护服务整体吞吐。