网络协议 TCP
文章目录
- 前言
- 滑动窗口
- 协定窗口缩放比
- 可靠性
- 三次握手
- 第一次握手
- 第二次握手
- 第三次握手
- 为什么需要三次握手
- 1 保证通信双方的发送和接收都没有问题
- 2 保证不会因为丢失的报文重新连接
- 四次挥手
- 为什么要等候2MSL?
- ARQ 协议
- 停止并等待ARQ协议(stop-and-wait)
- 连续ARQ协议(`Continuous ARQ`)
- SACK
- 可靠性总结
- 流量控制
- 拥塞控制
- 慢开始和避免拥塞
- 快恢复和快重传
前言
TCP
是传输层协议,具有如下特性:
1.可靠性
- 拥塞控制
- 流量控制
而拥塞控制
和流量控制
需要基于滑动窗口机制。
滑动窗口
消息的接收方: 都会有自己的处理能力上限。比如计算机B
一次性只能处理50
个消息,这个大小我们叫做窗口大小
。
消息的发送方:
消息的发送方也有一个自己发送大小窗口,当然这个大小受限于上层协议控制,比如流量控制和拥塞控制
,一般来说都会小于接收方的接收窗口。
我们以发送端的举例说明:
我们假设我们要发送的数据为HELLOWORD
,在网络传输中我们传输的数据会分多个片段然后发送。上图中 HEL
为TCP
协议中已发送且已经确认的数据,LOWO
为已发送但是没有收到确认的报文。RD
还未发送。
我们看下动图发送这个数据流程。
我们看到收到确认信息后,黄色窗口会像后移动,这种现象我们称为滑动窗口
.
tip: 滑动窗口大小会随时改变哦
协定窗口缩放比
在传输数据中双方会不断告知自己的窗口大小给对方。如下tcp报文格式
上图有一个16位窗口大小,但是这个数字要乘上syn所发协定的缩放比(可参考后文三次握手)
也就是传输的窗口大小要乘上syn所协定的比例大小。
可靠性
TCP 具有可靠传输的特性,如丢失一个报文会重传,在建立连接的时候会确认双方通信无障碍,关闭连接的时候确认双方都有收到。
三次握手
三次握手
用于确认通信前双方的发送功能和接收功能都是正常的。
我们首先看下如下图TCP通信流程。
图片来自 https://github.com/jawil/blog/issues/14
我们重新看下报文格式
第一次握手
我看下wireshark
客户端第一次发送链接的syn报文
我们这里只挑几个地方来讲
顺序号:
客户端会在syn
时生成一个随机数作为seq号,后续的报文序号根据此计数。所以第一个报文的sequence
的相对值是0.
FLAG
:
标志位将syn设置为1,表示这是一个链接请求
窗口大小:
注意这个数值需要乘上option中窗口缩放比
option
:
这个TCP
用于协商一些特性,如sack
等。
RFC132中timestamps说明
第二次握手
本次由服务端发出.
第三次握手
为什么需要三次握手
为什么连接需要三次?不是两次?
1 保证通信双方的发送和接收都没有问题
两次握手图
当客户端收到第二次握手包时可以证明两件事:
- 客户端可以感知:客服端可以正常发送到服务端,并且服务端可以正常接收和发送
- 服务端可以感知:客户端可以正常发送,服务端接收没问
但是服务端无法感知自己的发送能力是否正常,所以需要客户端重新在发送一次ack
也就是必须要三次握手确保客户端和服务端可以确认双发收发都正常。
2 保证不会因为丢失的报文重新连接
我们假设两次握手就可以链接的话,会出现以下情况
上图客户端发送第一个Syn
时因为网络原因滞留在网络中还没有发送到服务端。
所以客户端超时没收到ack,因此重发了一个Syn
然后服务端发送ack
,完成连接。
但是问题来了:
- 此时客户端和服务端突然完成通信然后断开连接.
- 滞留在网络的
Syn
包突然又发送到了服务端
因为服务端滞留的syn报文 发送到服务端,让服务端以为客户端又需要连接,所以发送ack报文后,直接连接完成。但是客户端是没有这种需求的,所以客户端会拒绝连接,但服务端会错误认为连接已经完成所以会不断发送信息。
四次挥手
挥手协议的目的:保证双方都收到的关闭的消息,并且留给一定的时间给给对方做处理。
我们来看下抓包处理情况:
客户端状态说明:
FIN_WAIT1
和 CLOSE_WAIT
客户端
发送FIN
报文给另一方,然后进入此FIN_WAIT1
状态,标记不在发送任何信息给对方。(除最后确认的ack
外) 。
服务端:
收到一个FIN
报文后,进入CLOSE_WAIT
状态,标记已经收到了对方想关闭链接的请求,但是可能我这边还需要处理资源释放的操作,所以先回复一个ACK
报文给对方,等接收真的可以断开连接的时候你在真的释放,因为接收方可能还会发送一些数据给发送发
。
FIN_WAIT2
客户端:
收到服务端的确认的报文,知道了对方我的关闭请求的对方已经知道了,我只需要静静的等候对方处理关闭请求,并等候对方FIN
报文,期间依然可以接收报文只是不发送而已,有可能对方在回收资源的时候需要客户端一起协助处理一些事情。
TIME_WAIT
和LAST-ACK
服务端
:
此时已经完成了所有的资源回收处理,已经可以发送FIN
告诉客服端,服务器可以随时断开连接,然后进入LAST-ACK
阶段
客户端
:
收到对方的FIN
报文,自己已经知道对方可以释放连接然后回一个ACK
包,并进入TIME-WAIT
状态,自己等候2MSL
的时间,如果2MSL
等候期间又收到了对方的FIN
包,证明自己发送的确认报文丢失。那么客户端会重复的发送确认报文然后重新等候2MSL
以上便是四次挥手的全部过程。
常见问题挥手面试:
为什么要等候2MSL?
- 客户端发送最后的ACK报文的时候有可能会丢失,服务端会因为超时等候而重发FIN报文
- 防止成功断开连接后又收到上次回话的报文。在网络传输的过程中,一个报文最大的生存时间是
MSL
,也就是超过这个时间后报文哪怕到了也要主动丢弃。但是如果一个TCP
请求完成后,立马重用又链接,可能会得到一个上次回话的报文。如果我们在断开连接的时候主动等候2MSL,可以确保本次回话的所有报文都已经过期,不会影响下次使用。
知乎 为什么TCP4次挥手时等待为2MSL?
ARQ 协议
arp
全称 Automatic repeat request
.包含:
- 停止并等待
ARQ
协议(stop-and-wait
) - 连续
ARQ
协议(Continuous ARQ
)
其核心思想有两点:
- 超时重传
- 确认
停止并等待ARQ协议(stop-and-wait)
核心思想有两点:
发送方收到确认在继续发送消息
超时重传
如果在一定时间内没有收到信息那么就重新发送信息
连续ARQ协议(Continuous ARQ
)
停止并等待ARQ协议(stop-and-wait)
存在一个问题就是在于信道带宽利用率过低的问题。
我们其实可以一次发送多个信息,然后对方只需要回复最后一个信息便表示收到之前的所有信息包。
但是上面会存在一个问题,如果我一次性发送 4个报文 编号为:1 ,2 ,3 ,4.
此时报文3
因为网络原因没有没收到,只收到1 2 4
那么回复确认报文只能为2
,因为发送方会重新发送 3 4 5 6
报文到服务端,只会让一些报文多次发送(回退N重传(Go-Back-N))。
为了解决(回退N重传(Go-Back-N))
问题,因此又推出了另一个协议叫做选择性确认SACK。
SACK
SACK
需要通信双方都支持才可以使用,确认支持是位于双方在SYN
阶段的时候协商,可以看文本三次握手阶段。
我买看下下面的两张图(转载自参考文献):
在没有启用SACK之前的丢包重传情况:
解释:
服务端连续发送seg1
,seg2
,seg3
,seg4
给客户端,但是seg2
丢失。所以在收到seg3
,seg4
将视为乱序报文,所以客户端立马回复两次ACK1
。服务端收到ACK1
后,其实不知道客户端收到了seg3
,seg4
,但是双方窗口又足够发送多余报文,所以用重新发送seg2
,seg3
,seg4
,但是冗余的发送带来性能消耗。
我们在看下启用SACK
后的丢包情况图:
客户端收到seg3
之后发送一个 ack1
并且附带一个sack3
标志,标识收到3
但是缺失了2
客户端收到seg4
之后发送一个 ack1
并且附带一个sack 3-4
标志,标识收到3和4,但是缺失了2
服务端整合两个ack和sack,判断出客户端丢失2但是收到了3和4,所以我只需要发送2给对方就好。
那么在数据传输中sack在哪?
sack数据会放入tcp数据包的option当中
格式如下:
乱序方会告诉发送方,自己收到的数据区间,上图两个一组,最大只能传输四组(option最大40字节)
比如收包的收到报文如下:
那么会发送如下sack的格式:
wireshark抓包如下(转载参考链接)
SACK参考文献
可靠性总结
- TCP通过三次握手确保通信双方通信能力没有障碍,且确保丢失报文重连问题
- TCP通过四次挥手确保双方都接收到了通知信息,并且有时间处理释放,且确保丢失报文会在下次重连时错误到底问题
- ARQ协议 保证每个报文都会正确传递到对方上,确保不会丢包等错误
流量控制
计算机A于计算机B的在处理网络数据包的时候都有自己上限,在双方发送TCP报文的时候会不断告诉对方自身的窗口大小,让发送方控制自己速率和大小。
如下下图的某TCP报文
这个报文告诉对方我的窗口大小是 501(需要自己乘上syn协商的窗口缩放比),不可以发送过大的数据。
通过不断的交换数据包告诉对方自己的接收能力,可以很好控制流量,防止爆发式的资源浪费。
拥塞控制
假设机算机A
和计算机B
通信的过程中某一路由节点发生了网络拥塞事件引起了大量的丢包,这时发送发应减少发送频率和数量防止加重拥塞和避免无用资源的浪费。请注意拥塞控制是指整个网络传输过程的,而流量控制是两个终端之间的。
举例情况:
由于路由器负载过大无法正常转发所有请求,所以只转发了第一个报文。如果计算机A
无视这一情况,不断发送大量的数据包,只会做徒劳的操作,还引起更多的网络堵塞。
为了应对这一情况我们首先推出了两个协议:
- 慢开始
- 避免拥塞
慢开始和避免拥塞
计算机A
要给计算机B
发送数据包,但是计算机A
不清楚发动数据包到计算机B
的网络是否顺畅,所以我们应该一开始少量发送数据包作为探测,因此我们定义了一个cwnd
变量,表示拥塞窗口大小,我们发送数据的大小由两个决定 ,一个是计算机B
接收能力rwnd
,和cwnd
。计算机A
的发送数据大小我们称为发送窗口大小swnd
。
综上我们可以得到以下公式:swnd=min(rwnd,cwnd)
发送大小在拥塞窗口和接收窗口中取最小值。
而慢开始协议是真对cwnd
变化指定的,所以我们只讨论cwnd
。
一开始,cwnd
应该是一个较小的数值,作为试探包。当我们收到确认的信息表示,当前网络顺畅,我们可以适当的提高cwnd
的大小,所以我们直接将cwnd
*2。
如下图:
无休止的增大拥塞窗口,会导致网络拥塞的情况,所以我们设置了一个阈值ssthresh
.
当拥塞窗口cwnd
大于等于ssthresh
时启用一个叫拥塞避免
算法。
拥塞避免
会在次成功收到ack
时,cwnd
会加一。相比起慢开始的倍数增长减缓了很多。
如下图:
当然不管怎么样网络出现网络丢包是早晚的事情,我们看下出现这类情况下TCP
的处理方式.
出现拥塞时ssthrehold
和cwnd
更新。ssthrehold
= 当前cwnd
/2
. (注意不管如何ssthrehold
必须大于等于2)cwnd
=1
快恢复和快重传
快重传
和快恢复
是对慢开始
和避免拥塞
的一种改良。
假设计算机A
发送一个数据包到计算机B
中途因为一些其他原因引起其中一个数据包丢失(比如传输过程受到了一些干扰引起某一数据包校验和失败),但是整体网络实际没有拥塞。如果直接判断丢包为网络拥塞
,然后降低cwnd
和ssthrehold
会极大影响网络整体交互体验。
所以TCP制定了新的策略来避免整这类情况。
计算机B
为接收方 ,计算机a
为发送方。
计算机B
发现乱序报文直接回复ack,而不是等到要发送数据的时候顺带带上ack
。
计算机a
连续收到三个失序的确认报文后立即重传丢失报文(快重传
),并启动快恢复
算法(实际会收到4个同一ack,至于为什么这样设计网上有轮子文章,这样设计较为合理)
为什么要收到失序报文后立即回复ack
?
要发送方更快的知晓网络状况,而不是等超时器触发,这样连续收到三个失序ack报文时直接回传丢失报文,这样提高了通讯效率。
为什么快重传
之后执行快恢复
?
能连续收到三个重复确认的ack
,证明网络不一定是拥塞,但是为了预防拥塞情况因此开始对cwnd
和ssthresh
进行调整。
快恢复执行以下操作:ssthresh=cwnd/2
cwnd=cwnd/2
然后继续执行拥塞避免算法
为什么快重传是是连续三次冗余的ack后触发https://blog.csdn.net/u010202588/article/details/54563648
还没有评论,来说两句吧...