TCP 协议详细总结
TCP协议详细总结
TCP(Transmission Control Protocol,传输控制协议)是OSI七层模型中传输层的核心协议,由RFC 793定义核心规范,后续通过多个RFC补充优化。它是面向连接、可靠交付、基于字节流、全双工的传输层协议,核心目标是在不可靠的IP网络之上,为应用层提供稳定、有序、无差错的端到端数据传输服务。
一、TCP核心基础特性
- 面向连接:通信前必须通过三次握手建立端到端的连接,通信结束后通过四次挥手释放连接,仅支持点对点单播,不支持广播/多播。
- 可靠交付:保证数据无差错、不丢失、不重复、按序到达接收方,通过序号、确认、重传等机制实现。
- 面向字节流:将应用层数据视为无结构的连续字节流,不保留报文边界,自动完成分片与重组,应用层无需关心底层传输细节。
- 全双工通信:连接建立后,双方可同时在两个方向上独立发送和接收数据,各自维护发送缓冲区与接收缓冲区。
- 流量控制:端到端的速率匹配,防止发送方发送过快导致接收方缓冲区溢出。
- 拥塞控制:全局网络适配,防止发送方发送过载导致网络中间节点拥塞崩溃。
二、TCP报文段(Segment)结构
TCP报文封装在IP数据报中,分为固定头部(20字节)和可选选项(最长40字节),因此TCP头部总长度最大为60字节。
| 字段 | 长度 | 核心作用与说明 |
|---|---|---|
| 源端口/目的端口 | 各16位 | 唯一标识通信两端的应用进程,端口范围0-65535;0-1023为熟知端口,1024-49151为注册端口,49152-65535为动态私有端口 |
| 序号(SEQ) | 32位 | 本报文段所携带数据的第一个字节的编号;SYN报文的序号为初始序号ISN,TCP字节流中每个字节都有唯一的32位循环序号 |
| 确认号(ACK) | 32位 | 期望接收的下一个字节的序号,仅当ACK标志位为1时有效;采用累计确认机制,代表该序号之前的所有字节已全部正确接收 |
| 数据偏移 | 4位 | 标识TCP头部长度,单位为4字节;最小值为5(对应20字节固定头部),最大值为15(对应60字节最大头部) |
| 保留位 | 6位 | 预留字段,必须全部置0 |
| 控制标志位(Flags) | 6位 | 核心控制位,每个位独立生效: • URG:紧急指针有效,标识紧急数据 • ACK:确认号有效,除初始SYN包外,所有报文必须置1 • PSH:推送标志,要求接收方立即将数据上交应用层,不等待缓冲区填满 • RST:重置连接,强制关闭异常连接,拒绝非法报文 • SYN:同步序号,用于连接建立 • FIN:结束标志,用于关闭连接,标识本方无数据再发送 |
| 窗口大小 | 16位 | 接收方通告的接收窗口rwnd,标识本方当前接收缓冲区的剩余可用空间,是TCP流量控制的核心;最大值为65535字节,可通过窗口扩大选项扩展 |
| 校验和 | 16位 | 强制校验,覆盖TCP头部、数据段和伪首部(含源IP、目的IP、协议号、TCP长度),用于检测报文传输过程中的差错,校验失败的报文会被直接丢弃 |
| 紧急指针 | 16位 | URG=1时有效,指向紧急数据的最后一个字节的序号,紧急数据会被优先传输,无需等待缓冲区排队 |
| 选项 | 可变长度 | 4字节对齐,最长40字节,用于TCP功能扩展,核心常用选项见下文 |
核心TCP选项
- MSS(最大报文段长度):TCP单报文段能承载的最大数据长度(不含TCP/IP头部),通常为以太网MTU(1500字节)-IP头(20字节)-TCP头(20字节)=1460字节,连接建立时协商,避免IP层分片。
- 窗口扩大因子(Window Scale):RFC 1323定义,解决16位窗口最大65535字节的限制,将窗口大小左移扩大因子位(最大14位),最大可支持1GB的窗口,适配高带宽长距离网络。
- SACK(选择确认):RFC 2018定义,弥补累计确认的缺陷,接收方可告知发送方已收到的不连续字节块,发送方仅重传真正丢失的报文段,大幅提升重传效率;D-SACK(重复SACK)扩展可告知发送方重复接收的报文段,优化乱序/丢包判断。
- 时间戳(Timestamps):RFC 1323定义,两个核心作用:① 精准计算RTT,避免重传确认二义性;② PAWS机制,防止32位序号回绕导致的新旧报文混淆。
- NOP:无操作,用于填充选项,保证4字节对齐。
三、TCP连接管理
TCP连接的唯一标识是四元组(源IP、源端口、目的IP、目的端口),完整生命周期分为连接建立、数据传输、连接释放三个阶段,基于有限状态机实现。
3.1 连接建立:三次握手
默认客户端为主动打开方,服务端为被动打开方,核心目标是双向同步初始序号ISN,验证双方收发能力正常,避免无效连接。
- 第一次握手:客户端发送SYN报文(SYN=1,ACK=0,seq=ISN(c)),客户端进入
SYN_SENT状态。 作用:向服务端发起连接请求,同步客户端的初始序号。 - 第二次握手:服务端收到SYN报文后,回复SYN+ACK报文(SYN=1,ACK=1,ack=ISN(c)+1,seq=ISN(s)),服务端进入
SYN_RCVD状态。 作用:确认客户端的连接请求,同时同步服务端的初始序号。 - 第三次握手:客户端收到SYN+ACK报文后,回复ACK报文(ACK=1,ack=ISN(s)+1,seq=ISN(c)+1),客户端进入
ESTABLISHED状态;服务端收到该ACK后,也进入ESTABLISHED状态。 作用:确认服务端的初始序号,连接正式建立,可开始双向数据传输。
高频核心问题:为什么是三次握手,不是两次?
- 防止失效的连接请求导致资源浪费:若客户端发送的SYN报文在网络中滞留,超时后重发SYN并建立连接,后续滞留的SYN到达服务端,两次握手会让服务端直接建立连接,等待客户端数据,浪费服务端资源;三次握手可通过客户端的最终确认,过滤无效SYN。
- 确保双向收发能力正常,同步双方ISN:TCP是全双工协议,需要双方都确认对方的发送/接收能力正常,且双向的初始序号都得到对方确认。两次握手仅能完成客户端到服务端的单向序号同步和能力验证,无法确认服务端到客户端的发送能力和序号有效性。
3.2 连接释放:四次挥手
TCP全双工的特性决定了双方的发送通道需要独立关闭,FIN报文仅能关闭本方的发送方向,因此需要四次交互完成双向关闭。默认客户端为主动关闭方,服务端为被动关闭方。
- 第一次挥手:客户端发送FIN报文(FIN=1,ACK=1,seq=u),客户端进入
FIN_WAIT_1状态。 作用:告知服务端,客户端已无数据发送,请求关闭客户端到服务端的发送通道。 - 第二次挥手:服务端收到FIN后,回复ACK报文(ACK=1,ack=u+1,seq=v),服务端进入
CLOSE_WAIT状态;客户端收到该ACK后,进入FIN_WAIT_2状态。 作用:确认客户端的关闭请求,告知客户端已收到关闭指令,但服务端可能还有数据未发送完成,需等待数据传输完毕再关闭。此时客户端→服务端的发送通道关闭,服务端→客户端的通道仍正常,进入半关闭状态。 - 第三次挥手:服务端数据传输完成后,发送FIN报文(FIN=1,ACK=1,seq=w,ack=u+1),服务端进入
LAST_ACK状态。 作用:告知客户端,服务端已无数据发送,请求关闭服务端到客户端的发送通道。 - 第四次挥手:客户端收到FIN后,回复ACK报文(ACK=1,ack=w+1,seq=u+1),客户端进入
TIME_WAIT状态;服务端收到该ACK后,立即进入CLOSED状态。客户端等待**2MSL(最长报文段寿命)**后,无重传报文到达,也进入CLOSED状态,连接完全释放。
高频核心问题
-
为什么是四次挥手,不是三次? 全双工模式下,被动关闭方收到FIN时,可能还有未传输完成的数据,无法将ACK确认和FIN关闭报文合并发送,必须先回复ACK确认关闭请求,待数据传输完成后,再发送FIN关闭本方通道,因此需要四次交互。而三次握手时,服务端无数据传输,可将SYN和ACK合并发送。
-
TIME_WAIT状态的作用,为什么要等待2MSL? MSL是IP报文在网络中的最长存活时间,RFC建议为2分钟,实际系统通常设置为30s-2分钟。
- 确保最后一个ACK报文能被对方接收:若服务端未收到第四次挥手的ACK,会重传FIN报文,TIME_WAIT状态可让客户端接收重传的FIN并重新发送ACK,避免服务端一直处于
LAST_ACK状态无法关闭。 - 防止本次连接的滞留报文污染下一个同四元组的连接:2MSL的时间足以让本次连接的所有报文从网络中彻底消失,避免旧连接的报文被新连接误接收,导致序号混淆和数据错乱。
- 确保最后一个ACK报文能被对方接收:若服务端未收到第四次挥手的ACK,会重传FIN报文,TIME_WAIT状态可让客户端接收重传的FIN并重新发送ACK,避免服务端一直处于
四、TCP可靠传输核心机制
TCP的可靠传输,本质是通过序号与确认机制做基础,超时重传、快速重传做兜底,SACK做优化,最终实现字节流的有序、无差错交付。
4.1 序号与累计确认
- TCP为字节流中的每个字节分配唯一的32位序号,报文段的seq为该段第一个数据字节的序号。
- 接收方通过ACK报文的确认号,告知发送方已成功接收的最后一个字节的序号+1,代表该确认号之前的所有字节已全部正确接收,这就是累计确认。
- 累计确认的优势是实现简单,抗ACK丢包能力强;缺陷是无法告知发送方中间不连续的已接收数据,丢包时会导致不必要的重传,由SACK选项弥补。
4.2 超时重传与RTO计算
TCP发送一个报文段后,会启动重传定时器,若在**RTO(重传超时时间)**内未收到对应的ACK,就会重传该报文段。
- RTO计算标准(RFC 6298):
RTO基于RTT(报文往返时间)动态计算,避免固定超时时间导致的性能问题:
- 平滑RTT:
SRTT = (1-α)×SRTT + α×RTT采样值,α默认取1/8 - RTT偏差:
RTTVAR = (1-β)×RTTVAR + β×|SRTT - RTT采样值|,β默认取1/4 - 最终RTO:
RTO = SRTT + 4×RTTVAR,系统通常设置最小RTO为1s,最大为60s
- 平滑RTT:
- Karn算法:重传的报文段的RTT采样值不参与SRTT计算,解决“确认二义性”问题——无法判断收到的ACK是原报文的响应,还是重传报文的响应,避免RTO计算失真。
- 超时处理:发生RTO超时后,TCP判断网络发生严重拥塞,会执行:慢启动阈值ssthresh置为当前cwnd的1/2,cwnd重置为初始值,重新进入慢启动阶段,同时执行指数退避,下一次超时的RTO翻倍。
4.3 快速重传(Fast Retransmit)
RFC 5681定义,无需等待RTO超时,当发送方连续收到3个重复的ACK时,立即重传对应的丢失报文段,大幅降低丢包后的传输延迟。
- 触发逻辑:发送方发送1、2、3、4、5号报文,2号报文丢失,接收方收到1号后回复ack=2,后续收到3、4、5号报文时,仍持续回复ack=2,形成3个重复ACK。
- 核心优势:避免等待超时时间,将丢包重传的延迟从秒级降到毫秒级,提升传输效率。
五、TCP流量控制
流量控制是端到端的速率匹配机制,核心解决“发送方发送过快,接收方缓冲区溢出导致丢包”的问题,基于滑动窗口协议实现。
5.1 滑动窗口核心原理
- 接收方在每个ACK报文的窗口字段中,通告本方当前的接收窗口
rwnd(接收缓冲区剩余可用空间)。 - 发送方的发送窗口上限为
swnd = min(拥塞窗口cwnd, 接收窗口rwnd),确保发送的数据不会超过接收方的处理能力。 - 发送窗口内的字节分为4类:① 已发送并收到ACK;② 已发送未收到ACK;③ 未发送但允许发送;④ 未发送且不允许发送。收到新的ACK后,发送窗口向右滑动,将新的字节纳入允许发送范围。
5.2 关键补充机制
-
持续计时器(Persist Timer) 解决零窗口死锁问题:若接收方通告rwnd=0,发送方停止发送;若后续接收方更新rwnd>0的ACK报文丢失,发送方会一直等待,形成死锁。 解决方案:发送方收到零窗口通知后,启动持续计时器,超时后发送1字节的窗口探测报文,接收方回复当前窗口大小;若窗口仍为0,重置计时器继续探测,直到窗口恢复非零。
-
糊涂窗口综合征(Silly Window Syndrome) 问题场景:接收方缓冲区满,应用程序每次仅读取1字节,接收方通告rwnd=1,发送方就发送1字节的报文段,导致TCP头部20字节+数据1字节,网络效率极低。 解决方案:
- 接收方策略:不通告小于MSS或缓冲区一半的窗口,仅当可用空间达到阈值时,才通告非零窗口。
- 发送方策略:通过Nagle算法避免发送小报文段。
-
Nagle算法(RFC 896) 解决大量小报文段导致的网络带宽浪费问题,核心规则:
- 若发送窗口内的数据长度≥MSS,立即发送;
- 若当前没有已发送未确认的报文段,立即发送;
- 否则,将数据放入缓冲区,直到收到ACK或缓冲区数据达到MSS,再统一发送。
注意:Nagle算法与延迟ACK配合时,可能导致交互式应用(SSH、Telnet)出现额外延迟,可通过
TCP_NODELAY选项关闭。
-
延迟ACK(Delayed ACK) 减少ACK报文的数量,降低网络开销,核心规则:
- 不对每个报文段立即回复ACK,延迟最大200ms(Linux默认40ms),若延迟期间有数据要发送,将ACK与数据捎带发送;
- 最多每2个连续的报文段,必须回复一个ACK。
六、TCP拥塞控制
拥塞控制是全局网络适配机制,核心解决“发送方发送过快,导致网络中间路由器过载、缓存溢出、丢包”的问题,避免网络拥塞崩溃。核心标准为RFC 5681,后续演进了多个优化算法。
TCP发送方维护一个拥塞窗口cwnd,代表在未收到ACK的情况下,最多可发送的字节数,最终发送窗口swnd = min(cwnd, rwnd),拥塞控制的核心就是根据网络拥塞状态,动态调整cwnd的大小。
6.1 传统拥塞控制四大核心阶段(Reno算法)
-
慢启动(Slow Start)
- 连接建立初始,cwnd设置为较小的初始值(RFC 5681建议10个MSS),每收到一个ACK,cwnd增加1个MSS,每经过一个RTT,cwnd指数翻倍。
- 核心目标:快速探测网络可用带宽,“慢启动”指初始值小,而非增长速度慢。
- 退出条件:当cwnd≥慢启动阈值ssthresh时,进入拥塞避免阶段。
-
拥塞避免(Congestion Avoidance)
- cwnd从指数增长转为线性增长,每经过一个完整的RTT,cwnd仅增加1个MSS,无论收到多少个ACK。
- 核心目标:缓慢逼近网络最大带宽,避免快速增长导致网络拥塞。
-
快速重传 收到3个重复ACK时,立即重传丢失的报文段,无需等待RTO超时,判断网络为轻微拥塞,不进入慢启动,直接进入快速恢复阶段。
-
快速恢复(Fast Recovery)
- 触发快速重传后,将ssthresh置为当前cwnd的1/2,cwnd置为ssthresh + 3×MSS;
- 每收到一个重复ACK,cwnd增加1个MSS,继续发送允许的报文段;
- 收到确认丢失报文的新ACK后,将cwnd重置为ssthresh,进入拥塞避免阶段。
- 核心目标:避免轻微拥塞时直接进入慢启动导致的带宽利用率骤降。
若发生RTO超时,判断为网络严重拥塞,执行:ssthresh = cwnd/2,cwnd重置为初始值,重新进入慢启动。
6.2 主流拥塞控制算法演进
| 算法 | 核心特点 | 适用场景 |
|---|---|---|
| Reno | 传统标准算法,基于丢包判断拥塞,四大阶段实现 | 传统低带宽、低延迟网络 |
| NewReno | 优化Reno,支持一个窗口内多个丢包的处理,减少不必要的慢启动 | 通用网络,替代Reno |
| CUBIC | Linux默认算法,基于三次函数的增长曲线,高带宽下增长更平稳,抗震荡能力强 | 高带宽延迟积的有线网络、数据中心 |
| BBR | Google研发,基于瓶颈带宽和最小RTT的拥塞控制,不依赖丢包判断拥塞,最大化带宽利用率,最小化延迟 | 高带宽长距离网络、无线网络(抗随机丢包)、CDN/云服务 |
七、TCP核心扩展与优化
- TCP Fast Open(TFO,RFC 7413):允许在三次握手的SYN报文中携带应用数据,实现0RTT数据传输,大幅缩短HTTP等短连接的建立延迟。
- MP-TCP(MultiPath TCP,RFC 6824):支持一个TCP连接同时使用多个网络接口(如WiFi+5G),实现带宽叠加、网络无缝切换,提升传输可靠性和吞吐量。
- TCP头部压缩(TCP HC):针对无线网络优化,压缩TCP/IP头部,降低小包传输的开销,提升无线链路的带宽利用率。
八、TCP vs UDP 核心对比
| 特性 | TCP | UDP |
|---|---|---|
| 连接性 | 面向连接,需三次握手建立、四次挥手释放 | 无连接,无需预建立,直接发送报文 |
| 可靠性 | 可靠,保证数据无丢失、无重复、按序到达 | 不可靠,不保证交付、不保证顺序,无重传 |
| 传输模式 | 面向字节流,无报文边界,自动分片重组 | 面向数据报,有严格边界,不合并不拆分 |
| 控制机制 | 流量控制、拥塞控制、超时重传、SACK等全套机制 | 仅支持基础校验和,所有可靠性逻辑由上层应用实现 |
| 头部开销 | 固定20字节,最大60字节 | 固定8字节,开销极低 |
| 双工性 | 全双工 | 全双工 |
| 核心场景 | HTTP/HTTPS、FTP、SMTP、SSH、WebSocket等需可靠传输的场景 | 直播、音视频通话、实时游戏、DNS、NTP、广播多播等低延迟优先场景 |
九、TCP常见问题与排查方向
- 三次握手失败:防火墙拦截、服务端未监听对应端口、SYN半连接队列溢出(SYN洪水攻击)。
- CLOSE_WAIT状态过多:应用层代码缺陷,被动关闭方未调用
close()释放文件描述符。 - TIME_WAIT状态过多:短连接场景下主动关闭方频繁创建/释放连接,占用端口资源,可通过调整
tcp_tw_reuse等内核参数优化。 - 吞吐量低:MSS设置过小、未开启窗口扩大选项、拥塞控制算法不匹配、链路丢包导致频繁重传。
- 传输延迟高:Nagle算法与延迟ACK叠加、RTT过大、频繁重传、拥塞控制降速。
核心参考RFC文档
- RFC 793:TCP核心协议规范
- RFC 5681:TCP拥塞控制标准
- RFC 6298:TCP RTO计算标准
- RFC 2018:TCP SACK选择确认
- RFC 1323:TCP高带宽扩展(窗口扩大、时间戳)
- RFC 8312:BBR拥塞控制算法
- RFC 7413:TCP Fast Open快速打开