赞
踩
文章参考:
在开始阅读该博客之前,先要好好了解一下TCP报文头部
到底有那些信息,阅读后续内容时有任何模糊的地方都可以回来这里查看梳理
,接下来我来解释一下:
发送方端口
:发送网络包程序的端口号接收方端口
:接收网络包程序的端口号序列号
:也叫序号(seq),发送数据的顺序编号。发送方告知接收方数据包有多少个字节。确认号
:也叫ACK号(ack),接收数据的顺序编号。接收方告知发送方已经收到所有数据的第几个字节。首部长度
:也叫数据偏移量。表示数据部分的起始位置。保留
:暂时没用控制位
:也叫标志位。上图中蓝色部分,又叫标志位。
窗口大小
:表示接收方告知发送方窗口大小。校验和
:用于检查是否出现错误。紧急指针
:应紧急处理的数据位置。可选字段
:除了连接操作,很少使用该部分。除了可选字段
,其余TCP头部信息总大小固定为20字节。
TCP协议是面向连接
的协议。
我们使用浏览器向服务器发送HTTP请求时,传输层协议使用TCP协议的。所以浏览器如果想要通过网卡
将数据包发送到服务器,我们需要先进行连接管理
,也就是大名鼎鼎的三次握手、四次挥手(面试爱问)。
三次握手
四次挥手
监听
状态,客户端主动发送第一次握手请求,TCP头部标志位SYN
置为1,序列号seq
置为随机生成的数x,此时客户端进程状态变成SYN_SENT
,第一次握手发送完毕。(服务端确定:自己具备接收
客户端请求的能力)标志位SYN、ACK
置为1,序列号seq
置为随机生成的数y,确认号ack
置为x + 1,然后完成第二次握手的发送,发送完毕后服务端进程状态变成SYN_RVCD
,第二次握手完毕。(客户端确定:自己具备接收、发送
请求的能力)标志位ACK
置为1,序列号seq
置为x + 1,确认号ack
置为y + 1,第三次握手发送完毕,服务端接收后建立连接完毕,客户端与服务端的进程都进入ESTABLISHED
状态。(服务端确定:自己具备发送
请求给客户端的能力)标志位ACK
为1,序列号seq
为x的数据包。此时客户端进程进入FIN-WAIT-1
状态。状态位ACK
置为1,序列号seq
置为y,确认号ack
置为x + 1。发送完毕后服务端进入CLOSE_WAIT
状态,客户端接收到第二次挥手的请求进入FIN_WAIT_2
状态,此时客户端依旧可以接收服务端未发送完毕的数据。状态位SYN、ACK
置为1,确认号ack
依旧是x,序列号seq
置为z。第三次挥手发送完毕后,服务端进入LAST_ACK
状态。状态位ACK
置为1,序列号seq
置为x + 1,确认号ack
置为z + 1,此时客户端进入TIME_WAIT
状态,等待2MSL
后自动进入CLOSE
状态。服务端收到第四次挥手报文后会变成CLOSE
状态。(2MSL
:报文段的最长生存时间;用来等待在网络中被丢弃或产生的延迟、重复数据包被清除)TCP校验和(Checksum)
是一个端到端的校验和,由发送端
计算,然后由接收端
验证。其目的是为了发现TCP首部和数据在发送端到接收端之间发生的任何改动。如果接收方检测到校验和有差错,则TCP段会被直接丢弃
。
关于校验和不做过多解释,详细解释请参考这篇文章:https://blog.csdn.net/qq_15437629/article/details/79183076
通过
序号
和确认号
可以确认接收方是否接收到了网络包。
最开始,在讲解TCP头部信息时,有个属性叫做序列号
(或者叫序号
)。
TCP模块会对应用进程传递来的请求报文
信息进行分组
(根据MSS
长度为单位),然后为每个分组
封装对应的TCP报文头部
信息,分组中的每一个字节信息都有对应的序列号
,此时TCP头部中序列号
属性值 = 该分组中第一个字节数据的序列号
。
TCP头部有了序列号
信息,接收方
收到的数据就可以根据序列号
进行排序(防止乱序),出现重复序列号
的分组数据就可以进行去重
操作(丢掉)。
关于确认应答
其实就比较好理解,最开始讲解三次握手、四次挥手时经常提到一个ack
,就是确认号ack
,确认号就是:接收方
告知发送方
已经收到所有数据的第几个字节。这样发送方
就能够得知接收方
是否真的接收到的分组信息。
通过序列号
+ 确认应答
这种回合制
(一问一答)方式就能很好的保障双方数据报文传递的可靠性!
特殊情况下,数据包会出现丢失
和延迟
等情况,例如:接收方暂时宕机、网络抖动等…
TCP协议作为可靠的传输协议
自然要处理这些极端情况,总不能数据包丢失了就不要了吧?所以TCP有了重传机制
,常见的重传方式主要分为4种(其中重点介绍前2种):
超时重传
快速重传
SACK
D-SACK
超时重传
比较好理解,根据字面意思:如果发送方
长时间没有收到接收方
的确认应答
就会将数据包进行重传
操作,当然超时重传
并不是只针对发送方重传,接收方在返回响应数据包时也可能丢失
,也需要进行超时重传的操作。
具体来说就是发送方
在发送数据包后,会设置一个定时器
,定时器的时间叫做超时重传时间(RTO)
,该时间范围内未收到接收方
ACK标志位的TCP数据包,就会进行重传
操作。
当然RTO
时长会影响到 TCP 的性能,应该根据具体网络的实际状况动态地进行调整。
- RTO 设置太小:会导致数据不必要重传,增加网络负担。
- RTO 设置太大,会导致数据传输的延迟,降低吞吐量。
情况1:发送方
请求数据包丢失。
情况2:接收方
响应数据包丢失。
特殊场景:即便触发了超时重传
,重传的数据包很有可能再次超时
;此时TCP会将下一次重传的超时时间设置为当前RTO时长2
倍。这样还是会极大的影响TCP传输的整体性能,所以有了快速重传
这种方式。
快速重传
不是通过时间
来判断是否该进行重传操作,而是通过冗余ACK报文
确认是否出现了问题,才考虑是否要触发快速重传
。
如下图所示,多次响应的ACK报文确认号
都是报文段2的TCP头部确认号
,这样接收端
就明白该报文信息是有问题的!就触发了快速重传
功能。所以,快速重传
:是当收到三个相同的 ACK确认号报文时,会在定时器过期之前,重传丢失的报文段(seq2)。
说到这里,特别解释一下,上图中跟之前提到的确认应答
方式有些不同,上图这种属于连续发送请求,并不是必须等待应答后再发送下一次请求,这种方式叫做累计应答
,是TCP的滑动窗口
机制提供的功能,后续会提到!
关于
SACK
和D-SACK
重传方案细节,这里就不做详细介绍了,如果有兴趣可以参考其他文章。
TCP协议是通过滑动窗口
进行流量控制,在介绍滑动窗口之前,先通过一个场景了解一下什么是流量控制
。发送方
进行数据发送操作需要考虑一下接收方
的数据接收能力;如果发送方
数据发送太快,接收方
来不及接受处理,那就会导致接收方把大量数据包存放到缓冲区
中,缓冲区如果放慢那就将多余的数据包扔掉,这样势必会导致网络带宽资源的浪费。
所以,TCP通过滑动窗口
来控制发送的发送速率(窗口大小),从而让接收方更从容
的接收处理数据包!
在讲解3.2、快速重传
时有个场景,不知道大家注意到没有,就是当我们发送一次seq数据包时还未等到拿到确认号报文就可以再次发送下一次seq数据包,这跟我们之前提到的确认应答
好像有点冲突!
其实确认应答
这种回合制
交互方式效率还是很低的…如果往返时间(RTT)过长,网络的使用效率势必会受到极大的影响!所以能不能连续发送多次TCP分组,不需要等待响应再进行下次发送操作呢?所以TCP报文头中有个窗口大小
属性,该属性开辟一个缓冲区
(或者叫窗口),本质就是操作系统一块内存区域,用于存放发送出去的分组数据,不需要让发送方等待接收方的确认应答
数据包,发送方可以连续发送数据,只要不超出缓冲区(窗口)大小。
如果连续发送数据包过程中,个别数据包发送失败,可以通过下一次应答来确认,根本不需要重发
!这种连续发送TCP分组的机制,我们称之为累计应答
(通过窗口大小限制),如下图:
滑动窗口
是操作系统开辟的一个缓存区
,发送方如果没收到确认应答
TCP数据包,需要缓冲区
中保留已发送的数据。收到应答后从缓存区清除数据。
滑动窗口的大小(rwnd
)由接收方
来掌控主导
的。毕竟能接收处理多少字节数据,接收方
自己是心知肚明的,总不能吃饱了继续喂吧,那不得撑坏咯。如果发送方
的窗口大小rwnd
为0,那就不允许继续发送数据了(说明接收方还没处理完毕呢!),那么发送方
会不断的向接收方
进行询问
:你还有空间接收处理吗?如果接收方
有可用窗口了,就会告诉发送方
,发送方会根据接收方的响应调整维护接收方自己的滑动窗口,继续快乐的进行发送了!
发送方、接收方窗口调整流程如下(图片来自:小林Coding
)。
发送方
窗口:接收方
窗口:双方彼此进行交互,各自不断调整维护滑动窗口
缓冲区大小,这样就不会出现发送方发送能力远远大于接收方处理能力的场景,从而达到了流量控制
的目的!
该小节图片均来自于
小林Coding
!
上面解释完了流量控制
,接下来先来看看这二者有哪些相似、不同之处。
发送方
发送数据量大于接收方
接收处理数据的速度,从而导致数据丢失、重传,造成网络效率低下。发送方
将大量数据输入到网络链路中,从而导致网络传输链路拥挤堵塞,传输速率降低、延迟,同样会导致数据丢失、重传,网络效率低下。拥塞控制
主要就是为了防止:大量的数据包输入到网络中,让网络传输负载过大,导致大量的丢包、重传,恶性循环!
所以我们就需要控制发送方
,通过拥塞窗口cwnd
根据当前网络状态动态调整窗口大小,从而避免当网络链路阻塞
时,还大量的将数据包发送给接收方!一般情况下,在规定时间内没有收到接收方的确认TCP报文,就可以认为当前网络状况较为阻塞
。
拥塞控制
主要分为以下4种算法,接下来依次详细解释一下:
听名字就能理解:刚开始启动时候,要慢点向网络链路中输入数据,刚开始不能用力过猛
,应该先输入少量数据,然后判断网络拥塞状况不断的调整拥塞窗口cwnd
的大小。如果起初就直接注入大量的数据到网络中,很有可能直接导致网络拥塞堵塞的情况发生。关于窗口大小扩容:发送方每收到一个 ACK 确认报文,拥塞窗口 cwnd 的大小就会双倍增长:1,2,4,8,16.....
但是慢启动
这种成倍增长并不是一直生效的,慢启动
持续增长到一定阶段会进入到拥塞避免
阶段。那么什么时候进行切换呢?
这里要引出一个概念叫做慢启动门限 ssthresh
。
慢启动
算法。拥塞避免
算法例如:当前的ssthresh = 8,那么当拥塞窗口cwnd
大小达到慢启动门限ssthresh
,就从指数增长
变成线性增长
(慢启动
变成拥塞避免
)
可惜好景不长,拥塞避免
的线性增长慢慢也会导致网络进入拥塞状态,从而导致丢包现象的发生。之前提到过丢包时TCP会触发重传机制(超时重传 + 快速重传)。
超时重传
时的拥塞发生
算法:直接变成了慢启动
阶段…惨不忍睹!
快速重传
时的拥塞发生
算法:接收方接收到之前数据包三次相同的ACK触发
快速恢复
算法TCP会认为超时重传
比快速重传
的网络拥塞状况更严重,毕竟快速重传
的触发条件是:接收方收到三次相同的ACK包。
当发生快速重传
后会直接切换到快速恢复
算法:
拥塞避免
)Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。