TCP专题
一、TCP连接建立
1、TCP的四元组
TCP属于面向连接——在发送数据之前,需要先建立一条点到点的连接。
TCP的四元组:源IP地址、目标IP地址、源端口、目标端口。可以唯一的区分和标识一条TCP的连接。
2、TCP报文结构
序列号:TCP是基于”字节流“的协议,这个序列号就是字节流的编号。
确认序列号:接收方期望收到发送方发送的下一个字节的序列号。
首部长度:标识TCP报文头部的长度,因为存在选项字段,所以是可变长头部,最短是20个字节。
URG:紧急标记位。若置1,则启用紧急指针字段。
ACK:确认标记位。若置1,则启用确认序列号字段。
PSH:推送标记位。若置1,表示该报文不放入缓存空间,直接推送至进程。
RST:重置标记位。若置1,表示强制断开TCP连接,不用四次挥手。
SYN:同步标记位。若置1,表示请求建立连接,通常在三次握手的前两次用到。
FIN:终止标记位。若置1,表示正常断开TCP连接,需要四次挥手。
窗口大小:用于控制流量传输。
校验和:用于确认数据传输的完整性,还会构造伪首部(IP层部分信息)进行校验。
紧急指针:标识需要紧急处理的数据的长度。
选项:用于扩展TCP协议功能,允许在标准头部之外传递额外的控制信息或参数。
3、TCP三次握手
这里的客户端一般指的是先请求建立连接的一方。
第一次握手:客户端发送SYN报文,携带seq序列号。
第二次握手:服务器回复SYN-ACK报文,携带seq序列号和ack确认序列号。
第三次握手:客户端回复ACK报文,携带ack确认序列号。
建立连接过程:
- 客户端发送SYN报文,其中序列号都是随机生成的,可以防止序列号预测攻击以及避免旧报文的干扰;
- 服务器收到SYN报文,会为其分配缓存空间,并发送ACK-SYN报文,其中序列号是随机生成的,确认序列号为上一个接收的序列号+1,表示确认收到以及期望收到的下一个序列号;
- 客户端收到ACK-SYN报文时,此时客户端单方面建立连接,因此下次携带的ACK报文时可以携带数据。
4、TCP的建立状态
分析上面的三次握手流程图,同样地,这里的客户端一般指的是先请求断开连接的一方。
客户端:
- 关闭状态:在发送SYN请求建立连接之后,进入到下一个状态;
- SYN_SENT:等待服务器返回的SYN-ACK报文,收到后进入下一个状态;
- 建立完成状态:此时客户端指向服务器的会话已经建立,所以客户端发送给服务器的最后一个ACK报文是允许携带数据的。
服务器:
- 关闭状态:当服务器的应用程序创建一个监听的套接字,将进入到下一个状态;
- 侦听状态:当接收到客户端发送的SYN报文之后,为TCP连接分配缓存空间,同时发送SYN-ACK报文,进入到下一个状态;
- SYN_RCVD:等待客户端返回的ACK报文,收到后进入下一个状态;
- 建立完成状态:TCP双向会话均建立完成。
注意:在第一次握手时,若服务器没有开启listen侦听,在收到SYN报文时,会回复RST报文拒绝建立连接。
5、SYN泛洪攻击
SYN泛洪攻击属于Dos攻击。攻击者伪造大量源 IP 地址,向目标服务器发送大量 SYN 包。服务器回应 SYN-ACK 包后,因源 IP 虚假无法收到 ACK 包,致使连接资源被半开放连接大量占用,从而无法给正常用户提供服务。
防御SYN泛洪攻击:
- 防火墙代理:防火墙代理服务,并设置每目标IP代理阈值和每目标IP丢弃阈值。一开始,客户端正常访问服务器,达到代理阈值后,防火墙开始代理服务;达到丢弃阈值后,防火墙直接把数据包丢弃。
- SYN Cookie:收到SYN报文后,不再分配缓存空间,将四元组(源目IP和端口)再加上一个随机数经过hash算法生成一个摘要值,即SYN Cookie,使用SYN Cookie作为序列号回复客户端,同时保存摘要值+1的数值,判断客户端返回的Ack报文是否准确。
6、疑点解答
(1)为什么一定是三次握手?
三次握手相较于四次握手节省资源资源。
三次握手相较于二次握手,能够解决新旧连接造成资源混乱的问题。
二、TCP连接断开
1、TCP的四次挥手
这里的客户端一般指的是先请求断开连接的一方。
第一次挥手:客户端发送FIN报文。
第二次挥手:服务器回复ACK报文。
第三次挥手:服务器发送FIN报文。
第四次挥手:客户端回复ACK报文。
断开连接过程:
- 客户端发起FIN报文时,是可以携带最后一段数据的;
- 服务器回复ACK报文时,断开的是客户端指向服务器的单方面会话,此时,客户端不能给服务器发送数据,而服务器可以继续发送剩下的数据;
- 等待服务器将数据全部发送完后,再继续发送FIN报文,该报文也可以携带最后一段数据;
- 客户端回复ACK报文后,双方连接彻底断开。
2、TCP的断开状态
分析上面的四次挥手流程图,同样地,这里的客户端一般指的是先请求断开连接的一方。
客户端:
- 建立完成状态:发送完所有字节流后,携带最后一组字节流的数据段,同时FIN置1,进入下一个状态;
- FIN_WAIT_1:等待服务器回复ACK,收到ACK报文应答后,进入下一个状态;
- FIN_WAIT_2:等待服务器发起FIN,收到FIN报文后,回复ACK确认报文,进入下一个状态;
- TIME_WAIT:等待2MSL时间后进入下一个状态;
- 关闭状态: 断开TCP的连接,释放所有TCP连接占用的资源。
服务器:
- 建立完成状态:收到客户端发送的FIN断开请求后,服务器将回复一个ACK确认报文,进入到下一个状态。
- CLOSED_WAIT:等待服务器自身数据的发送,当自身所有数据传递完毕后,发送FIN断开请求,进入下一个状态。
- LAST_ACK:等待客户端进行最后的ACK应答,当收到客户端发送的ACK确认报文之后,进入到下一个状态。
- 关闭状态:断开TCP连接,释放所有TCP连接占用的资源。
3、疑点解答
(1)为什么要有TIME_WAIT
保证TCP会话可以正常关闭。
(2)为什么TIME_WAIT状态的时间是2MSL
MSL——报文最大存活时间
1.保证最后一个ACK报文丢失的情况下,能够等到对方重传,一来一回正好两个MSL时间,可以保证TCP会话正常断开。
2.设置2MSL,足以让两个方向上的报文都丢弃,再出现新的连接时,不至于被历史报文造成数据错乱。
3.太长会占用资源。
(3)为什么需要四次挥手
在服务器收到FIN报文时,可能还存在未发送完的数据,因此只回复了ACK报文,等待剩余的数据发送完毕则发送FIN报文断开连接。
四次挥手过程是理论上的形式,实际情况中,存在三次挥手,即服务器正好发完了数据,将直接回复ACK-FIN报文。
三、TCP可靠性传输
1、排序机制
首先引入两个概念:
- 分片:网络层的IP协议执行,当数据包超过 MTU 时,会将其拆分成多个分片传输。
- MTU:最大传输单元,默认为1500。
分段:TCP是一款基于字节流的协议,可对数据进行分段,以及可以确保数据适合下层(IP层)的 MTU 限制。
MSS:最大段长度。TCP在建立三次握手时,前两个SYN报文中携带该参数,并且双方的该参数允许不同,若不同,则将按照数值最小的执行。MSS最大值为1460(1500 - 20 - 20)
而排序机制则是基于序列号来完成的 ,序列号seq代表字节流中第一个字节的编号。如下图所示,第一个seq=0,数据长度为1000,则字节流范围为0-999,所以下一次发送的序列号seq=1000。
2、确认机制
确认机制则是基于确认序列号来完成的,确认序列号ack是期望收到的下一个seq序列号。如下图所示,在收到对方第一个seq=0,且数据长度为1000,则回复ack=1000,期望对方发送的下一个seq序列号,同时也确认自己收到了完整的数据包。
3、超时重传
超时重传机制指的是本端在一定时间内,没有接收到对端反馈的确认报文而出发的重传机制。
RTO:超时重传时间。
RTT:往返时间。指的是本端将数据发出后,到他接收到对端反馈的确认报文之后的这一段时间。
RTO值在计算时,需要略大于RTT,因为RTT是一个可变化的值,所以RTO也是一共动态变化的值。
超时间隔加倍机制:
每次发送重传报文之后,会将RTO设为上一次RTO的两倍时间。
4、快速重传
接收方收到一个数据段中的序列号大于自己期望的序列号,称为失序报文。
通过冗余ACK(Duplicate ACK),将缺失的报文段连续发送三遍,这时发送方收到后触发快速重传机制,将seq=200的报文重新发送。
5、SACK
选择确认机制(SACK):在快速重传机制中,接受方在回复冗余ACK时,里面将携带自身已经接收到的数据信息,避免重复发送造成资源浪费。
抓包:
6、三次握手的可靠性保障
第一次握手丢失: 客户端将按照定义RTO值(默认1s)进行重传,并将RTO值设为双倍,若达到最大重传次数时,还未收到回复包,则断开连接进入CLOSE状态。
第二次握手丢失:客户端和服务器都会触发超时重传机制。
第三次握手丢失:服务器触发超时重传机制。
7、四次挥手的可靠性保障
第一次挥手丢失:客户端等待RTO时间进行重传,并将RTO值设为双倍,若达到最大重传次数时,还未收到回复包,则断开连接进入CLOSE状态。
第二次挥手丢失:客户端触发超时重传机制。服务器只会等待对方再次发送FIN报文才会重新发送ACK报文。
第三次挥手丢失:服务器触发超时重传机制。客户端也只会等待对方再次发送FIN报文才会重新发送ACK报文。
第四次挥手丢失:服务器触发超时重传机制,客户端收到后重新发送ACK报文,并重置2MSL时间。
四、TCP流量控制
1、滑动窗口
窗口:TCP报文(来自对端)里的窗口大小是指无需等待确认应答ack,而可以连续发送的数据包的最大值。
接收窗口(rwnd):表示的是接收方此时缓存空间可用的大小,且缓存空间中存放的数据是可变化的,所以rwnd也随之变化,这个机制就叫做滑动窗口,且最开始时,rwnd=RcvBuffer。
2、窗口关闭
窗口关闭指的是接收方发送一个窗口值为0的数据报文段,表示接收方自身的缓存空间已经占满,无法再进行数据接收,发送方收到后将不再发送数据。
但由于ACK报文丢失不会触发重传机制,则会导致发送方因未收到窗口更新而长期阻塞。
为了避免上述问题,在窗口关闭时,客户端会周期性发送窗口探测报文(携带1字节数据)。
3、小窗口的处理
小窗口是指接收方通告的可用窗口(rwnd
)非常小(如几个字节甚至 1 字节),导致发送方每次只能发送少量数据。因此接收方和发送方分别制订了一套应对规则。
接收方:会设定一个通告窗口的最小值,一般为MSS和1/2缓存空间中的较小值,若通告的窗口值小于最小值,则将通告一个窗口值为0的数据报文段来关闭窗口,直到窗口大小突破最小值。
发送方:一般采用延时处理,只有满足下面条件之一才能发送数据,否则将囤积数据。
- 窗口大小(rwnd&cwnd) >= MSS 且数据大小 >= MSS;
- 收到之前发送数据的ACK回报;
五、TCP拥塞控制
1、拥塞判断
拥塞控制:TCP会观察网络的拥堵情况,如果网络拥堵严重,则降低发送量,以缓解网络拥塞的情况。
拥塞判断:
以下行为都为连接中的丢包行为,视为拥塞的表现。
- 数据包确认超时(超时重传)
- 收到接收方发送的3个冗余ack(快速重传)
2、拥塞控制算法
拥塞窗口(cwnd):表示是发送方根据当前网络拥塞程度动态调整的窗口。
发送方发出未收到确认的字节数必须小于或等于rwnd和cwnd的最小值。其中rwnd是接收方设立并通告,cwnd是发送方设立并通告。
TCP的拥塞控制算法(自计时的协议):慢启动、拥塞避免、快速回复。
3、慢启动
慢启动:最开始cwnd=1MSS,每收到一个新的ACK确认报文(重传的确认报文不计算在内),cwnd就会增加一个MSS的大小。
慢启动门限(ssthresh):
- cwnd < ssthresh 时,使用 慢启动算法。
- cwnd = ssthresh 时,使用 慢启动算法/拥塞避免算法。
- cwnd > ssthresh 时,使用 拥塞避免算法。
4、拥塞避免
拥塞避免:在一个RTT(往返时间)内,cwnd只增长一共MSS。
5、快速恢复
数据包确认超时处理:首先将ssthresh值设置为当前cwnd值的一半,之后将cwnd设置为1个MSS,按照新的ssthresh值,执行慢启动算法,达到门限值后,重新进入到拥塞避免算法中。
收到接收方发送的3个冗余ACK处理:首先将ssthresh值设定为当前cwnd值的一半,之后直接基于ssthresh值执行拥塞避免算法。