02_TCP协议的三次握手、四次挥手和重传机制
一、TCP三次握手
TCP有6种标示:SYN(建立连接); ACK(确认); PSH(传送); FIN(结束); RST(重置); URG(紧急) 。

第一次握手:客户端向服务器端发送连接请求包SYN(syn=j),等待服务器回应。
第二次握手:服务器端收到客户端连接请求包SYN(syn=j)后,将客户端的请求包SYN(syn=j)放入到自己的未连接队列,此时服务器需要发送两个包给客户端。
- 向客户端发送确认自己收到其连接请求的确认包ACK(ack=j+1),向客户端表明已知道了其连接请求;
- 向客户端发送连接询问请求包SYN(syn=k),询问客户端是否已经准备好建立连接,进行数据通信;
- 此时服务器进入SYN_RECV状态
第三次握手:客户端收到服务器的ACK(ack=j+1)和SYN(syn=k)后,向服务器发送连接建立的确认包ACK(ack=k+1),回应服务器已建立连接,此时客户端进入ESTABLISHED状态,服务器收到ACK后也进入ESTABLISHED状态,可以开始进行数据传输。
三次握手的目的:消除旧有连接请求的SYN消息对新连接的干扰,同步连接双方的序列号和确认号并交换TCP 窗口大小信息。
二、TCP数据的传输过程

(1)主机A初始seq为1200,滑动窗体为100,向主机B传递数据的过程;
(2)假设主机B在完全成功接收数据的基础上,那么主机B为了确认这一点,向主机A发送 ACK 包,并将 Ack 号设置为 1301。因此按如下的公式确认 Ack 号:Ack序号= Seq序号 + 传递的字节数 + 1 (这是在完全接受成功的情况下)
(3)主机A获得B传来的ack(1301)后,开始发送seq为1301,滑动窗体为100的数据。
如果存在丢包怎么办?
下面分析传输过程中数据包丢失的情况,如下图所示:

(1)上图表示通过 Seq 1301 数据包向主机B传递100字节的数据,但中间发生了错误,主机B未收到。经过一段时间后,主机A仍未收到对于 Seq 1301 的ACK确认,因此尝试重传数据。
(2)为了完成数据包的重传,TCP套接字每次发送数据包时都会启动定时器,如果在一定时间内没有收到目标机器传回的 ACK 包,那么定时器超时,数据包会重传。
三、TCP四次挥手

第一次挥手:客户端发送一个FIN(结束),用来关闭客户到服务器的连接
- 客户端进程发出连接释放报文,并且停止发送数据;释放数据报文首部,FIN=1,序列号为seq=u(u=传送数据的最后一个字节序号+1)。
- 此时客户端进入FIN-WAIT-1(终止等待1)状态。FIN报文段消耗一个序号。
第二次挥手:服务端收到这个FIN,他发回一个ACK(确认),确认收到序号为收到序号+1
- 服务器收到FIN,发送确认报文,ACK=1,ack=u+1,并带上自己的序列号seq=v;服务器进入CLOSE-WAIT(关闭等待)状态。
- 这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。
- 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务端发送的最后的数据)。
第三次挥手:服务端发送一个FIN(结束)到客户端,服务端关闭客户端的连接
- 服务器发送一个FIN(结束)到客户端,服务器关闭客户端连接。
- 服务器将最后的数据发送完毕后,就向客户端发送连接释放报文,FIN=1,ack=u+1,此时序列号为seq=w。
- 服务器进入LAST-ACK(最后确认)状态。
第四次挥手:客户端发送ACK(确认)报文确认,并将确认的序号+1,关闭完成
- 户端收到服务器的连接释放报文后,必须发出确认,ACK=1,ack=w+1,序列号是seq=u+1;客户端进入TIME-WAIT(时间等待)状态。
- 经过最长报文段寿命的时间后,客户端撤销TCP连接,才进入CLOSED状态。
- 服务器只要收到了客户端发出的确认,立即进入CLOSED状态
客户端突然挂掉了怎么办?
正常连接时,客户端突然挂掉了,如果没有措施处理这种情况,那么就会出现客户端和服务器端出现长时期的空闲。解决办法是在服务器端设置保活计时器,每当服务器收到客户端的消息,就将计时器复位。超时时间通常设置为2小时。若服务器超过2小时没收到客户的信息,他就发送探测报文段。若发送了10个探测报文段,每一个相隔75秒,还没有响应就认为客户端出了故障,因而终止该连接。
四、TCP的重传机制
4.1 为什么需要重传机制
在数据传输过程中,数据包的丢失是不可避免的,有很多原因都会导致丢包:
- 网络拥塞导致路由器缓冲区溢出,丢弃数据包
- 信号干扰导致数据包损坏,接收方校验失败后丢弃(checksum 校验失败)
- 路由器故障或链路中断导致数据包未能到达目的地
- 接收方处理能力不足,来不及处理而丢弃数据包
为了应对各种情况,TCP 需要能够检测到丢包,并重发丢失的数据包。
TCP 通过确认应答(ACK)和超时重传这两种基本机制来实现可靠传输。
4.2 TCP 确认应答(ACK)
什么是确认应答?
- 发送方发送数据时,为每个数据段分配一个序列号(Sequence Number)
- 接收方收到数据后,返回一个确认号(Acknowledgement Number),表示期望收到的下一个字节序号
- 发送方通过接收到的确认号,可以判断数据是否已经成功到达接收方

如图:
- 发送方先发送第一个数据包:序列号 SEQ = 100,长度50字节,也就是发送了100 到 149 这段数据
- 接收方收到后回复:ACK = 150,意思是"我收到了100-149 的数据,请发送150 开始的数据"
- 发送方继续发送第二个数据包:序列号 SEQ = 150,长度50字节,也就是发送了150 到 199 这段数据
- 接收方再次回复:ACK = 200,意思是"我收到了150-199 的数据,请发送 200 开始的数据"
上图简化为一方发送,另一方接收数据,实际上 TCP 是全双工协议,双方可以同时发送/接收,同时发送时,ACK 就携带在数据包中。
4.3 TCP 超时重传
数据包在传输过程中仍然可能会出现两种问题:数据包丢失和ACK 确认包丢失。
如何解决这个问题呢?
超时重传:在发送数据包之后启动一个定时器,如果在指定重传超时时间(RTO, Retransmission Timeout)没有收到 ACK,就认为数据包丢失了,需要重新发送

- RTO 太短:可能导致不必要的重传,浪费带宽
- RTO 太长:丢包后等待时间过长,影响传输效率

理想的 RTO 应该略大于网络往返时间(RTT),既要避免过早重传,也要保证丢包时能及时恢复。
并且RTO 必须是动态调节的,以适配不同的网络链路情况。
参考值:RTT(Round-Trip Time)数据包从发送到收到确认 ACK 的往返时间
(1)TCP RTO 计算: Jacobson / Karels 算法(RFC6298)
该算法采用指数加权移动平均(EWMA)的方法来平滑 RTT 的波动,通过计算 RTT 的偏差来动态调整超时时间,使 RTO 的计算更加准确和灵活。
计算流程:
- RTT采样:测量数据包往返时间
- 计算平滑往返时间(SRTT)
- 计算往返时间偏差(DevRTT)
- 计算最终RTO
算法优点:
- 自适应性强:通过 EWMA 方法,能够自动适应网络状况的变化
- 抗干扰性好:DevRTT 的引入使算法对网络抖动具有更好的容忍度
- 实用性高:参数选择经过充分验证,适用于大多数实际场景,现在 TCP 协议中用的就是这个算法
(2)TCP RTO 计算:Karn 算法
前面 RTO 的计算方法中,往返时间 RTT 的测量都是非常关键的。
但是在发生重传时,由于无法确定收到的ACK是对应原始包还是重传包,因此无法准确计算RTT,这就是著名的Karn现象。

为解决这个问题,Karn提出了一个算法:在计算加权平均往返时间 SRTT 时,不使用发生过重传的报文段的 RTT 样本。
具体来说:当发生重传时,不更新 SRTT 的计算,因此 RTO 也不会更新,这避免了使用不准确的 RTT 样本。
但是,Karn 算法也带来了新的问题:当网络条件发生显著变化,导致报文段的传输时延突然增大并持续较长时间时,由于原有的 RTO 不足以覆盖新的传输时延,会触发重传。而按照 Karn 算法,重传报文段的 RTT 样本不被采用,导致 RTO 无法及时调整以适应新的网络状况。这种情况下,报文段会反复重传,严重影响传输效率。
因此,对 Karn 算法进行修改:报文段每重传一次,就把超时重传时间 RTO增大一些。典型的做法是将新 RTO 的值取为原 RTO 值的 2 倍。
最终计算流程:
- RTT采样(发生超时忽略)
- 计算平滑往返时间(SRTT)
- 计算往返时间偏差(DevRTT)
- 计算初始RTO
- 超时重传的RTO调整:出现超时时RTO=RTO*2

浙公网安备 33010602011771号