TCP 与连接管理

TCP 为什么可靠

TCP 通过以下机制提供可靠、有序的字节流:

  • 序列号和确认应答。
  • 超时重传与快速重传。
  • 滑动窗口流量控制。
  • 拥塞窗口和拥塞控制。
  • 校验和与有序重组。

“可靠”不等于“请求一定成功”。连接可能超时、中断,应用层仍然需要重试、幂等和错误处理。

三次握手

两次握手无法让服务端确认客户端已经收到自己的序列号,也更容易让历史失效连接造成资源浪费。第三次 ACK 让双方确认收发能力和初始序列号同步完成。

典型过程如下:

客户端                      服务端
  SYN(seq = x)       ───────>
  SYN(seq = y) + ACK(x + 1) <───────
  ACK(y + 1)         ───────>

四次挥手与 TIME_WAIT

TCP 是全双工协议,两个方向需要分别关闭,因此常见过程是四次挥手。

主动关闭方进入 TIME_WAIT,主要用于:

  1. 确保最后的 ACK 丢失后仍能重新发送。
  2. 等待旧连接报文在网络中消失,避免污染相同四元组的新连接。

长连接、短连接与连接复用

  • HTTP/1.0 默认更接近短连接。
  • HTTP/1.1 默认使用持久连接,但并发请求常需要多个 TCP 连接。
  • HTTP/2 在一个连接上多路复用多个流。
  • HTTP/3 在 QUIC 连接内为不同流提供相对独立的可靠传输,不再直接使用 TCP。

队头阻塞

  • HTTP/1.1 管线化:前一个响应阻塞后一个响应。
  • HTTP/2:应用层流可并发,但 TCP 丢包会阻塞整个连接的数据交付。
  • HTTP/3:QUIC 的不同流不会因某个流丢包而全部等待。

TCP 粘包与拆包

TCP 是面向字节流的协议,不保留应用层消息边界。发送方多次发送的数据,接收方可能一次读取到;一次发送的数据,也可能分多次读取,这就是常说的粘包和拆包。

解决方式由应用层定义消息边界,例如:

  • 固定长度消息。
  • 在消息前增加长度字段。
  • 使用特殊分隔符。
  • 使用具备明确边界的应用层协议,如 HTTP 的请求头和消息体。

HTTP 通常不需要业务代码自己处理 TCP 粘包,因为 HTTP 解析器会根据 Content-LengthTransfer-Encoding 或协议帧格式还原消息边界。

高频追问

  • TCP 为什么需要三次握手而不是两次?
  • TCP 为什么挥手通常需要四次?
  • HTTP keep-alive 与 TCP keepalive 是一回事吗?
  • HTTP/2 为什么仍然存在 TCP 队头阻塞?