TCP 协议
TCP(Transmission Control Protocol,传输控制协议)是面向连接的、可靠的、基于字节流的传输层协议。TCP 解决了数据传输的可靠性、顺序性、流量控制、拥塞控制等问题。
TCP 头部格式
源端口和目的端口
16 位,标识发送方和接收方的进程。端口号范围:0-65535,0-1023 为知名端口(如 HTTP 80、HTTPS 443)。
序列号
32 位,标识 TCP 段中第一个字节的序号。序列号是循环使用的,达到 2^32-1 后从 0 开始。
确认号
32 位,期望收到的下一个字节的序号。确认号是累积确认,表示之前的字节都已正确接收。
数据偏移
4 位,TCP 头部的长度,以 4 字节为单位。最小值 5(20 字节),最大值 15(60 字节)。
标志位
URG(紧急指针有效)、ACK(确认号有效)、PSH(推送)、RST(重置连接)、SYN(同步序列号)、FIN(结束连接)。
窗口大小
16 位,接收窗口的大小,用于流量控制。窗口大小最大 65535 字节,通过窗口扩大选项可以扩大到 1GB。
校验和
16 位,检验 TCP 头部和数据的完整性。发送方计算校验和,接收方验证校验和。
紧急指针
16 位,指向紧急数据的最后一个字节。紧急数据需要优先处理。
选项
选项字段可变长,常见选项:MSS(最大段大小)、窗口扩大、时间戳、SACK(选择性确认)。
TCP 连接建立
三次握手
第一次握手:客户端发送 SYN,seq = x。客户端进入 SYN_SENT 状态。
第二次握手:服务端回复 SYN+ACK,seq = y,ack = x + 1。服务端进入 SYN_RCVD 状态。
第三次握手:客户端回复 ACK,seq = x + 1,ack = y + 1。双方进入 ESTABLISHED 状态。
为什么需要三次握手
防止失效的连接请求报文段突然又传送到服务端,产生错误。
客户端的发送连接请求报文段丢失,客户端重传连接请求报文段,连接建立后数据传输完毕,连接释放。第一个丢失的连接请求报文段延迟到达,服务端误认为新的连接请求,发送确认报文段,进入 ESTABLISHED 状态,等待客户端数据,浪费资源。
三次握手可以确认双方的接收和发送能力都正常,同步双方的初始序列号。
SYN 攻击
SYN 攻击是攻击者发送大量 SYN 报文段,不完成三次握手,占用服务端的半连接队列,导致服务端无法处理正常连接。
防御措施:SYN Cookies(不保存半连接状态,根据 SYN 计算 Cookie)、缩短超时时间、增加半连接队列大小、开启 tcp_syncookies。
TCP 连接终止
四次挥手
第一次挥手:主动方发送 FIN,seq = u。主动方进入 FIN_WAIT_1 状态。
第二次挥手:被动方回复 ACK,ack = u + 1。被动方进入 CLOSE_WAIT 状态。主动方收到 ACK 后进入 FIN_WAIT_2 状态。
第三次挥手:被动方发送 FIN,seq = w,ack = u + 1。被动方进入 LAST_ACK 状态。
第四次挥手:主动方回复 ACK,seq = u + 1,ack = w + 1。主动方进入 TIME_WAIT 状态,等待 2MSL 后进入 CLOSED 状态。被动方收到 ACK 后进入 CLOSED 状态。
为什么需要四次挥手
TCP 是全双工协议,双方都可以发送数据。主动方关闭发送通道,但可能还在接收数据。被动方可能还有数据要发送,需要确认数据发送完毕后再关闭发送通道。
TIME_WAIT 状态
TIME_WAIT 状态持续 2MSL,约 60 秒。作用:确保最后的 ACK 能够到达被动方,如果 ACK 丢失,被动方会重传 FIN,主动方可以重发 ACK。确保旧连接的报文段在网络中消失,不影响新连接。
TIME_WAIT 状态的问题:占用端口资源,高并发短连接场景下端口可能耗尽。解决方案:开启 tcp_tw_reuse、tcp_tw_recycle(Linux 4.12 已移除)、增加端口范围。
TCP 可靠传输
序列号
序列号标识 TCP 段中第一个字节的序号,用于数据排序、去重、流量控制。
序列号是随机生成的,防止序列号猜测攻击。
确认应答
接收方收到数据后发送 ACK,确认号是期望收到的下一个字节的序号。ACK 是累积确认,表示之前的字节都已正确接收。
超时重传
发送方发送数据后启动定时器,如果超时未收到 ACK,则重传数据。超时时间根据 RTT(Round Trip Time,往返时间)动态调整,RTT 变化大时超时时间也变化大。
快速重传
接收方收到失序报文段后立即发送重复 ACK,发送方收到 3 个重复 ACK 后立即重传报文段,不必等待超时。
选择性确认
SACK(Selective ACK)允许接收方告诉发送方哪些报文段已正确接收,哪些报文段丢失,发送方只需重传丢失的报文段,提高效率。
TCP 流量控制
滑动窗口
接收方通过窗口大小告诉发送方自己可以接收多少数据,发送方根据窗口大小调整发送速率,避免淹没接收方。
窗口大小是接收缓冲区的剩余空间,接收方处理数据后窗口增大,接收方缓冲区满时窗口为 0。
零窗口问题
接收方窗口为 0 时,发送方停止发送数据,定期发送零窗口探测报文段,询问窗口是否打开。
接收方窗口打开后发送 ACK,通知发送方可以继续发送数据。
TCP 拥塞控制
慢启动
连接建立后,拥塞窗口从 1 个 MSS 开始,每收到一个 ACK,拥塞窗口增加 1 个 MSS,指数增长。慢启动阈值(ssthresh)是慢启动和拥塞避免的分界线,拥塞窗口超过 ssthresh 后进入拥塞避免。
拥塞避免
拥塞窗口每 RTT 增加 1 个 MSS,线性增长,避免拥塞。
快速重传
收到 3 个重复 ACK 后,立即重传报文段,不必等待超时。
快速恢复
快速重传后,不进入慢启动,而是将拥塞窗口设置为 ssthresh 的一半,进入拥塞避免。
TCP 是互联网的核心协议,理解 TCP 的连接建立、连接终止、可靠传输、流量控制、拥塞控制,有助于理解网络编程的底层原理。