TCP连接是客户端和服务器之间通过三次握手建立的一条可靠通信链路。TCP连接由源IP、源端口、目的IP和目的端口四个信息唯一确定。建立连接后,TCP通过序列号、确认应答、超时重传等机制保证数据可靠、有序传输,通信结束时通过四次挥手释放连接。
为什么需要三次握手?
三次握手主要是为了确认双方的发送和接收能力都正常,并同步双方的初始序列号,避免历史连接请求造成错误连接。
TCP为什么可靠?
TCP通过序列号保证数据有序,通过 ACK 确认数据是否收到,通过超时重传保证数据不丢失,同时还有流量控制和拥塞控制保证通信稳定。
HTTP和TCP是什么关系?
HTTP属于应用层协议,TCP属于传输层协议。HTTP负责定义请求和响应格式,TCP负责建立连接并可靠传输数据。一次HTTP请求通常需要先建立TCP连接。
TCP是用来解决什么问题?
TCP主要是为了解决网络通信中数据不可靠的问题。
由于底层IP网络是不保证可靠传输的,数据可能出现丢失、乱序、重复等情况
TCP通过建立连接、序列号、确认应答、超时重传、流量控制和拥塞控制等机制,保证数据能够可靠、有序地传输
TCP 可靠性的实现细节
- 序列号:每个字节都有编号,接收方按编号排序重组,乱序到达的包也能正确还原
- 确认号:接收方回复 ACK 说“第 N 个字节之前的都收到了”,发送方就知道哪些数据安全送达了
- 超时重传:发出去一定时间没收到 ACK,默认丢了,重发。超时时间会根据网络 RTT 动态调整
- 快速重传:连续收到 3 个重复 ACK,不等超时直接重发,减少等待时间
- 校验和:检测数据有没有在传输中被破坏,坏了就丢掉让发送方重传
滑动窗口与流量控制
发送方不是发完一个包等 ACK 再发下一个,那样效率太低。滑动窗口允许发送方一次发多个包,不用等每个包的 ACK。
接收方在 ACK 里带一个窗口大小字段,告诉发送方“我的缓冲区还能装 X 字节”。发送方就控制住,未被确认的数据不能超过这个窗口大小。
如果接收方处理不过来,窗口会变小甚至变成 0,发送方就得停下来等。等接收方处理完了窗口变大,再继续发。这样就避免了接收方被淹没。
拥塞控制算法演进
拥塞控制是为了避免发送方发太快把网络搞崩。经典算法有四个阶段:
- 慢启动:刚开始不知道网络带宽多大,窗口从 1 开始,每收到一个 ACK 窗口加 1,一个 RTT 后窗口翻倍,指数增长,快速探测网络容量
- 拥塞避免:窗口超过阈值后,改成线性增长,每个 RTT 窗口加 1,小心翼翼地探测上限
- 快速重传:收到 3 个重复 ACK,立刻重传丢失的包,不等超时
- 快速恢复:快速重传后,阈值减半,窗口设为阈值,进入拥塞避免阶段,而不是从 1 重新慢启动
随着网络发展,出现了很多改进算法:TCP Reno、TCP NewReno、TCP Vegas、CUBIC、BBR 等。BBR 是 Google 提出的,不再依赖丢包来判断拥塞,而是通过测量带宽和延迟来调整发送速率,在高带宽高延迟网络下效果很好。
TCP 的局限性
- 队头阻塞:一个包丢了,后面收到的包都得在缓冲区等着,即使后面的包和丢的那个没关系。HTTP/2 多路复用在 TCP 上跑,一个流丢包会阻塞所有流
- 握手延迟:三次握手至少 1.5 个 RTT,对于短连接来说开销占比大
- 协议僵化:TCP 在内核实现,想改进得等操作系统升级,中间设备也可能不认新特性
这些问题催生了 QUIC 协议,在 UDP 上实现可靠传输,把拥塞控制、多路复用都搬到应用层,一个流丢包不影响其他流,握手时间也缩短了。
更多 QUIC 细节参见《HTTP/1.0、HTTP/1.1、HTTP/2 与 HTTP/3 有什么区别》。