TCP知识点
1. TCP和UDP的区别
- 连接:TCP是面向连接的,在传输数据前要先建立连接;UDP是无连接的
- 服务对象:TCP是一对一的两点服务;UDP支持一对一、一对多、多对多的通信
- 可靠性:TCP是可靠交付数据,能保证数据无差错、无丢失、无重复、按序到达接收方;UDP是尽最大努力交付,不保证可靠性
- 拥塞控制和流量控制:TCP会通过拥塞控制和流量控制来决定数据的发送效率;而UDP没有,即使网络拥堵,也不会影响UDP的发送效率
- 首部开销:TCP在不适用选项字段时头部长20B;UDP头部固定是8B
- 传输方式:TCP是面向字节流的传输;UDP是基于报文的传输
- 分片策略不同:TCP的数据如果大于MSS,会在传输层进行分片,主机收到后也在传输层组装TCP数据包;UDP的数据如果大于MTU,会在IP层进行分片,主机收到后,在IP层组装完数据
- 应用场景:
- TCP能保证数据的可靠性交付,经常用于:FTP文件传输、HTTP/HTTPS
- UDP的处理高效简单,经常用于:
- 总包量少的通信,如DNS
- 视频、音频等多媒体通信
- 广播通信
2. TCP建立连接
(1)三次握手过程
初始阶段,客户端和服务端都处于
CLOSE
状态,服务端开始主动监听某个端口,处于LISTEN
状态客户端请求建立连接,会随机初始化序号
client_isn
填充报文头部:将
client_isn
置于TCP首部的序号字段,把SYN位标志为1将报文发送给服务端
进入
SYN_SENT
状态服务端收到SYN报文,随机初始化序号
sever_isn
填充报文头部:将
sever_isn
置于序号字段,将client_isn + 1
填充到确认应答号字段,将SYN和ACK位置为1将报文发送给客户端
进入
SYN_RCVD
状态客户端收到SYN-ACK报文,回应最后一个应答报文
填充报文头部:序号字段为
client_isn
,确认应答号字段为sever_isn + 1
,将ACK位置为1可以携带客户端到服务端的数据
将报文发送给服务端
进入
ESTABLISHED
状态服务端收到ACK报文,也进入
ESTABLISHED
状态
TCP的连接状态在linux中通过
netstat -napt
命令查看
(2)为什么是三次握手
- 三次握手才能阻止历史连接/冗余连接
- 场景示例:客户端发送了一个SYN(isn=10)报文后宕机,而且这个报文还被网络堵塞了;客户端重启之后重新向服务端发送一个新的SYN(isn=20)报文,旧的报文比新的报文先到达。先看三次握手的情况:SYN(10)到达服务端之后,服务端回复SYN+ACK(ack=11)报文,客户端收到之后发现不是自己期望收到的确认号(21)就会回RST报文,服务端收到RST报文后释放连接;如果只有两次握手的话,服务端收到SYN报文后就进入
ESTABLISHED
状态,就会给对方发送数据,造成资源浪费 - 总结:两次握手无法阻止历史连接/冗余连接,每接收到一个SYN报文就会建立一次连接,因为服务端没有中间状态
SYN_RCVD
来给客户端终止历史连接,导致服务端可能建立一个历史连接或者是无效冗余连接,造成资源浪费
- 场景示例:客户端发送了一个SYN(isn=10)报文后宕机,而且这个报文还被网络堵塞了;客户端重启之后重新向服务端发送一个新的SYN(isn=20)报文,旧的报文比新的报文先到达。先看三次握手的情况:SYN(10)到达服务端之后,服务端回复SYN+ACK(ack=11)报文,客户端收到之后发现不是自己期望收到的确认号(21)就会回RST报文,服务端收到RST报文后释放连接;如果只有两次握手的话,服务端收到SYN报文后就进入
- 同步双方初始化序列号
- TCP通信双方都得维护一个序列号,作用
- 去除重复数据
- 根据序列号按序接收
- 可以通过对方回复的ACK中的确认应答号来标识哪些数据包已被接收
- 只有三次握手,一来一回才能确保双方都发送自己的序列号并且确认自己的序列号已被对方接收,并且服务端对SYN的回复以及通知对方自己序列号的报文可以合并,所以是三次握手
- TCP通信双方都得维护一个序列号,作用
(3)序列号问题
序列号作用
- 方便接收方去除重复数据
- 方便接收方根据序列号按序组装报文
- 接收方可以根据收到的报文中的确认应答字段知道发送的报文中哪些已经被接收
为什么每次建立连接的初始化序列号要求不一样
- 主要原因:防止历史报文被下一个相同四元组连接接收
- 背景:TCP四次挥手中的TIME_WAIT状态回持续2MSL时长,历史报文会在网络中消失。但是我们不能保证每次连接都能通过四次挥手来正常关闭连接,例如:服务端断电重启,回复客户端RST报文,这次建立的连接终止,然后服务端和客户端又建立了相同的四元组连接。
- 假如每次序列号都是从0开始,大概率会导致旧的报文序列号正好在接收端的接收窗口内,会被正常接收,造成数据错乱;所以每次初始化序列号不一样能够很大程度上避免历史报文被下一个相同的四元组连接接收
序列号的初始化方法
ISN = M + F(四元组)
- 计时器:每隔4微秒+1
- F是一个Hash算法,根据四元组生成一个随机值
随机数基于时钟计时器递增,基本上不会随机成一样的初始化序列号
序列号回绕问题
- 问题:序列号是一个32位的无符号数,当到达最大值之后会循环回到0,无法仅根据序列号来判断新老数据
- 解决方法:TCP时间戳
- TCP时间戳是一个默认开启的选项,作用:1)便于精确计算RTT,2)防止序列号回绕
- 原理:连接双方维护一次最近收到的数据包的时间戳,当收到新数据包时会读取时间戳并跟维护的最新时间戳比较,如果不是递增的,就说明该数据包时过期的,直接丢弃这个数据包
(4)IP层分片,为什么TCP层还需要MSS?
- MTU:网络包的最大长度,一般是1500B;MSS:除去IP头和TCP头,容纳的TCP数据的最大长度
- 如果传输层不分片,到IP层再分片的话,那对于传输层来说发送的是一整个报文,接收的也是一整个报文。如果发生丢失触发超时重传,整个IP报文的所有分片都得重传。而在传输层分片的话,只需要重传丢失的报文分片
3. TCP断开连接
(1)四次挥手过程
- 客户端打算关闭连接,向服务端发送
FIN
报文,之后进入FIN_WAIT_1
状态 - 服务端收到
FIN
报文后,回复ACK
报文,进入CLOSE_WAIT
状态 - 客户端收到
ACK
报文后,进入FIN_WAIT_2
状态 - 等待服务端处理完数据之后,向客户端发送
FIN
报文,进入LAST_ACK
状态 - 客户端收到
FIN
报文后,回复ACK
报文,进入TIME_WAIT
状态 - 服务端收到
ACK
报文后,就进入CLOSE
状态,至此服务端连接关闭 - 客户端在经过2MSL时间后,自动进入
CLOSE
状态,客户端连接也关闭
注意:主动关闭连接的才有
TIME_WAIT
状态
(2)为什么是四次挥手
关闭连接时,客户端向服务端发送 FIN
报文,只能标识,客户端不再发送数据了但还能接收数据
服务端收到并回复 ACK
报文后,可能还有数据需要处理和发送,只有等到服务端不再发送数据时,才会发送 FIN
报文。
所以,服务端通常需要等待完成数据的处理和发送,所以 ACK
和 FIN
报文一般都会分开发送,因此需要四次挥手
(3) TIME_WAIT
状态
TIME_WAIT
等待时间 = 2MSL
- MSL:报文最大生存时间
- 网络中可能存在来自发送方的数据包,被接收方接收后又会回复响应报文,一来一回需要等待2MSL时间。所以相当于至少允许报文丢失一次。
- 从客户端接收到
FIN
发送ACK
后开始计时,如果在TIME_WAIT
时间内客户端的ACK
没有传输到服务端,服务端重传FIN
报文,客户端重新回复ACK
,并且2MSL
重新计时
(4)为什么需要 TIME_WAIT
状态
- 防止历史连接中的数据被后面相同四元组的连接错误的接收
- 如果没有
TIME_WAIT
状态或者时间过短,比如说,上一个连接中最后发送的报文被网络延迟了,后面双方建立了新的相同的四元组连接,当被阻塞的报文到达接收方后,如果正好处于接收窗口内会被正常接收,导致数据错乱 - 因此设计
TIME_WAIT
状态,并会持续2MSL时间,足以让两个方向上的数据包在网络中自然消失
- 如果没有
- 保证被动关闭连接的一方,能被正常关闭
TIME_WAIT
作用:等待足够的时间以确保最后的ACK能被服务器接收,从而正常关闭- 场景示例:如果没有
TIME_WAIT
,客户端发送完ACK报文就进入关闭状态,如果这个ACK报文丢失,服务端会重传FIN报文,但是这个时候客户端处于关闭状态,没有办法正常回复ACK,会回复RST报文。服务端收到RST报文后因为异常而进入CLOSE状态
(5)TIME_WAIT
过多的危害、如何优化
- 危害:
- 占用系统资源
- 占用端口资源
- 优化思路:资源复用
(6)服务器出现大量 TIME_WAIT
状态的原因
TIME_WAIT
是主动关闭连接方才会出现的状态,所以服务器出现大量的TIME_WAIT
状态,说明服务器主动断开了很多TCP连接
Q:什么场景下服务端会主动断开连接
HTTP没有使用长连接(任意一方没有开启长连接就会使用短连接)
当完成一次TCP通信后就会关闭,一般是由服务端主动关闭TCP连接,服务端就会出现
TIME_WAIT
状态因此,当没有启用长连接时,服务端在处理完一个http请求后就会主动关闭连接,导致出现大量
TIME_WAIT
状态HTTP长连接超时
服务器会指定HTTP的超时时间,如果这段时间内客户端没有再发起新的请求,服务端就会关闭连接,出现
TIME_WAIT
状态所以,服务端出现大量
TIME_WAIT
状态可能是大量的客户端建立TCP连接后长时间没有发送数据,导致服务端超时主动关闭连接,出现大量TIME_WAIT
状态HTTP长连接请求数量达到上限
服务器会定义一条长连接上最大能处理的客户端请求数量,当超过最大限制时就会主动关闭连接,从而出现
TIME_WAIT
状态
4. 重传机制、滑动窗口、流量控制和拥塞控制
(1)重传机制
TCP实现可靠传输的方式之一,是通过序列号与确认应答
如果数据包丢失,会用重传机制
常见的重传机制:
超时重传:当超过一定时间没有收到确认应答报文,就会重传数据
超时重传时间RTO略大于RTT
问题:每次都要时间等待,引入快速重传
快速重传:不以时间为驱动,而是以数据为驱动
工作方式:收到三个相同的ACK报文就会重传该报文
- 三次相同的ack报文:说明该报文对方还没收到,不用等到超时再重传
(2)滑动窗口
确认应答的缺点:等待应答后再发送下一个数据包,效率低,网络吞吐量小,引入窗口
窗口:无需等待确认应答,可以连续发送数据的最大值
(3)流量控制
发送方根据接收方实际接收能力控制发送的数据量
(4)拥塞控制
背景:网络发生拥堵时,如果继续发送大量数据包,可能会导致数据包时延、丢失,发送方就会重传数据,导致网络的负担更重,进入恶循环
拥塞控制的目的:避免发送方的数据填满整个网络
拥塞窗口cwnd:发送方维护的一个状态变量,根据网络的拥塞程度动态变化
- 变化规则:网络没有拥塞就增大,网络出现拥塞就减少
发送窗口swnd = min(cwnd, rwnd)
四个算法
慢启动:初始化为1,指数性增长
当拥塞窗口达到慢启动门限,启用拥塞避免算法
拥塞避免:cwnd线性增长
一直增长以后,网络慢慢进入拥塞状态,就会出现丢包,触发重传机制,也就是进入了拥塞发生算法
拥塞发生
重传机制由两种:超时重传和快速重传
发生超时重传(RTO时间内没收到确认应答报文),就会使用拥塞发生算法:门限值=拥塞窗口*1/2,拥塞窗口恢复初始值
接着就开始慢启动算法
发生快速重传(连续收到三次相同的ACK报文):拥塞窗口=拥塞窗口*1/2,门限值=拥塞窗口
接着进入快恢复算法
快恢复
- 拥塞窗口=门限值+3
- 重传丢失的数据包