TCP协议
/ / 点击前言
今天看到一个分享,有讲到使用http请求时踩到了一个坑:服务端
TIME_WAIT过多导致服务变得不可用。
通过这个案例来复习一下tcp协议,并分析原因和解决问题。
TCP协议
简介
TCP协议面向应用层提供一种面向连接的、可靠的字节流服务,它处于7层网络协议的第4层。
- 面向连接意味着两个应用在使用TCP协议交互时要首先建立连接(后续将讲到如何建立连接和关闭连接)
- 可靠的服务表明由TCP协议交付给应用的数据是可靠的。可靠性指的是数据能够被
正确地有序地传输
协议首部
![]()
协议头部第一行,分别表明了TCP双方的端口号,结合IP层的源、目的地址IP可唯一确定一个TCP连接双方的信息。
第二行表示当前数据包编号,为TCP传输过程中标记数据编号,它用于TCP提供可靠数据传输时保证数据有序的依据
第三行表示对对方数据包的确认号。值为收到的数据编号+1
第四行,前4位表示TCP协议头部长度(限制了头部最长2^4-1字长,1个字长为32bit),6位保留位之后为6位标志位:
- URG 紧急指针有效
- ACK 确认序号有效
- PSH 接收方应尽快将报文段交给应用层
- RST 重建连接
- SYN 同步序号发起一个连接
- FIN 发送端完成发送任务
第四行后16位为窗口大小,用于TCP数据传输过程中拥塞控制
第五行为校验和与紧急指针。
选项中字段:TODO
数据包可能为空
TCP选项
TCP首部可包含选项部分。每个选项若干字节,00、01 为不带len字节的选项。带len字节的选项第一个字节表示类型,第二个字节表示该选项配置的长度len,后续跟len-2个字节的选项值
- 00 选项表结束
- 01 无操作
- 02 xx 最大报文段长度,后续跟着的xx个字节数据表示长度具体值
- 08 0a 时间戳,后续4字节时间戳值,4字节时间戳回显应答
TCP连接
连接过程:三次握手
为了建立TCP连接,客户端程序与服务端程序共进行过程:
这个过程共有3次交互,也被叫做3次握手。
![]()
如图,大写
SYNACK表示的TCP协议头中的标志位,小写req,ack表示TCP协议头中的传输数据编号和确认编号(握手过程中syn标志位一直为1)。
首先看连接过程:
- 客户端发起数据传输连接请求,
seq值为x- 服务端收到客户端连接请求,回复确认
ACK标志位为1,序号ack=x+1,同时发起服务端到客户端的连接请求,seq值位y(标志位ACKSYN为1,seq值为y,ack值为x+1)- 客户端收到确认请求,建立起 客户端-》服务端连接。并回复服务端连接请求,
ACK标志位为1,确认序号ack=y+1,当服务端收到确认序号时,建立起 服务端-》客户端 连接
连接过程中的状态变化:
客户端:
- 一开始处于
CLOSE状态,当主动发起连接请求后,状态变为SYN_SENT- 当服务端收到请求并回复
ACK,客户端收到返回包并回复服务端ACK后,状态由SYN_SENT变为ESTABLISHED,客户端-》服务端 连接建立服务端:
- 一开始处于
CLOSE状态,当被动接受连接请求后,状态变为SYN_RCVD- 当客户端收到请求并回复
ACK,服务端收到ACK后,状态由SYN_RCVD变为ESTABLISHED,服务端-》客户端连接建立至此,全双工的TCP连接建立完成,可以进行数据传输
断开过程:四次挥手
断开一个连接需要经过四次交互,这是因为TCP连接是全双工工作,每个数据传输方向需要单独关闭。
当一方完成数据传输时就可以发送一个FIN来终止这个方向的连接,接收FIN的一端收到信号后不再读数据但是仍可以发送数据,可以等数据发送完毕再发FIN信号进行关闭该方向的连接。![]()
断开连接时交互过程:
- 客户端数据发送完成后,向服务端发送
FIN信号表明结束C->S方向的连接- 服务端收到
FIN信号,回复ACK,客户端收到ACK,C->S方向连接正式关闭- 服务端数据发送完成向客户端发送
FIN信号关闭S->C方向连接- 客户端收到
FIN信号,回复ACK,服务端收到ACK,S->C方向连接正式关闭
断开连接过程中的状态变化:
主动断开方(图中的客户端):
- 一开始处于
ESTABLISHED状态,当主动发起关闭连接请求后状态变为FIN_WAIT_1(等待对端响应)- 当收到确认响应后,状态由
FIN_WAIT_1变为FIN_WAIT_2(等待对端关闭连接)- 当收到对端的关闭请求后,状态由
FIN_WAIT_2变为TIME_WAIT(等待2个MSL[最大报文寿命,报文在网络传输过程中最大生存时间]超时)- 当经过2个MSL超时后,状态由
TIME_WAIT变为CLOSE被动断开方(图中的服务端):
- 一开始处于
ESTABLISHED状态,当收到关闭连接请求后,状态变为CLOSE_WAIT- 当服务端数据传输完毕,向对端发起关闭请求连接后,状态变为
LAST_ACK- 当收到对端
ACK消息后,状态由LAST_ACK变为CLOSE主动断开方
TIME_WAIT存在的必要性:
如果没有2MSL的TIME_WAIT状态,当主动断开方发送的ACK失败时,另一端会重发FIN请求,而这时,如果原来的连接被复用就会出问题。有了TIME_WAIT状态后,主动断开方的ACK应答如果在网络中丢失,那么另一端会重发FIN请求,它只需再次应答即可。当2个MSL时间后仍未收到重发的请求,则认为应答数据包正确到达对端。
TCP可靠性传输
可靠性保证
- 应用数据被分割成TCP认为最合适发送的数据段(区别于UDP数据包长度不变),由TCP传给IP的信息单位成为报文段或段,后续将讲到如何确定报文段的长度。
- 当TCP发送一个段后,它会启动一个定时器,等待目的端确认收到报文段。若不能及时收到确认将会重发报文段
- TCP收到另一端的数据后,它将发送一个确认,这个确认不是立即发送的,通常推迟几分之一秒(捎带ACK,如果同时需要发送数据给对端,将ACK捎带过去)
- TCP将保持它的首部和数据的检验和,这是一个端到端的检验和,目的是检测数据在传输过程中的任何变化。如果收到的段和检验和有差错,TCP将丢弃该段,等待发送端超时重传
- IP层交付的数据可能是失序的,TCP层会根据收到的数据重新排序,将数据以正确的顺序交付给上层
- IP层有可能收到重复数据,TCP会丢弃掉重复的数据
- TCP提供流量控制。TCP的对端都有固定大小的缓冲空间,TCP的接收端只允许另一端发送接收端缓冲区所能容纳的数据,这防止较快主机发送到慢主机时导致缓冲区溢出
最大报文段长度
最大报文段长度(MSS)表示TCP往另一端传输数据的最大块数据的长度。当开始建立连接的时候,连接的双方都会告知各自的MSS。MSS的值在TCP协议头的
选项部分。
超时重传
内核参数
net.ipv4.tcp_timestamps控制tcp数据包里是否带有timestamp,如果带有,则接收到消息的时候会做时间过滤,不符合的会被丢掉。
拥塞控制
滑动窗口

报文在发送过程中可能的状态:
- 已发送
- 可发送,但未发送
- 可发送,已发送,但未收到ack
- 不可发送
TCP发送数据的时候,会指定滑动窗口大小,如上图,滑动窗口大小为6,首先,看传输过程中,滑动窗口在3-8报文段时的情况。此时,1,2报文段处于状态①,9之后的报文段处于状态④;在窗口3-8内,都是可发送的报文,有处于状态1、2、3的,当客户端收到报文段5的ack时,3-5报文状态变为1,此时滑动窗口向右滑动3个位置,变成6-11。数据传输的过程中,发送窗口不断随着网络情况调整大小。
拥塞控制算法
拥塞控制主要通过一定的算法,不断修改拥塞窗口大小(cwnd,congestion window)达到目的。主要用到以下几种算法:
- 慢启动
- 拥塞避免
- 快速重传: 当发送数据端连续收到3次同一个数据包ack时,进入快速重传。此时 ssthresh值设为当前cwnd的一半,
慢启动
cwnd从初始值1开始,每收到1个报文段ACK,cwnd增加1.(数据并发发送,在一定时间内如果无丢包,cwnd几乎呈指数级增加。起始值为1,收到这个包的ACK后cwnd+1,下次发送2个数据包,当2个数据包收到ACK后cwnd+2,再下次发送4个数据包,以此类推)
当cwnd值增加到ssthresh大小时,进入拥塞避免。进入慢启动过程有两种情况:
- 刚刚建立连接,开始发送数据: 刚开始时,ssthresh值由连接双方协商,cwnd值为1。然后不断增加cwnd值,到达ssthresh值大小时进入拥塞避免
- 链路发生拥塞,超时未收到ack: 发生拥塞时,ssthresh值变为cwnd一半,cwnd值变为1。然后通过慢启动算法不断增加cwnd,到达ssthresh值大小时进入拥塞避免
拥塞避免
当cwnd的值到达慢启动ssthresh的值后,每个RTT(Round Trip Time,往返时延)cwnd+1。在拥塞避免阶段cwnd值线性增加
进入拥塞避免有两种情况:
- 由慢启动过程的2中情况转入拥塞避免:在慢启动中已列出2种情况
- 由快速重传状态转入拥塞避免:发生快速重传时,ssthresh值大小变为cwnd的一半,cwnd值也变为原来的一半(此时ssthresh与cwnd一样大)。
拥塞避免最后转换为2种状态:
- 变为慢启动: 当发生超时时,状态转换为慢启动。
- 变为快速重传: 当发生连续收到3个相同ack时,状态转换为快速重传
快速重传
进入快速重传的情况:
- 连续收到3次同一个数据包的ack:收到3次ack包很可能是由丢包引起的(在数据接收端,当数据包N丢失后,后续收到N+1,N+2的数据包时都会重复向数据发送端发送对N-1的ACK包。如果2次重复ACK,有可能是数据包乱序到达,3次重复ACK,很可能是丢包,4次,很大可能丢包。但是判断丢包用的重复ack次数值越大,对网络性能影响越大。折中取了3次)
图示
- 状态转换

- 状态转换实例图
