目录
- TCP如何实现可靠传输?
- TCP如何实现流量控制?(滑动窗口)
- TCP如何实现拥塞控制?(慢开始、拥塞避免、快重传、快恢复)
引申:
1.UDP能否也实现可靠传输?
2.TCP为什么要三次握手、四次挥手?两次握手、3次挥手行不行?
3.什么情况使用UDP好、什么情况使用TCP好?
1. TCP如何实现可靠传输
TCP的可靠传输是基于连续ARQ协议的,ARQ协议中有两个重要的概念:滑动窗口和累计确认。但是我认为TCP能实现可靠传输不仅仅是靠连续ARQ协议,还依靠了:
1.通过三次握手、四次挥手来保证信道的可连接性 ;
2.采用停止等待协议、连续ARQ协议(自动重传)来保证数据的正确性;
3.序列号和确认应答号保证了数据的有序性,
4.校验和:如果收到字节的检验和有差错,TCP 将丢弃这个报文段和不确认收到此报文段。
1.1 TCP三次握手的必要性
TCP通过三次握手来确定这是一个可靠的连接,三次握手的目的是为了确定客户端和服务端都有正常的【收发】能力,即客户端可以发送和接收消息,服务器也可以发送和接收消息。
- 第一次:客户端 → 发送请求 ( SYN = 1,syn = x )→ 服务端,服务端知道【客户端】可以【发送】消息;
- 第二次:服务端 → 应答+请求 ( ACK = 1 , SYN = 1,syn = y , ack = x + 1)→ 服务端,客户端知道【服务端】可以【接收】自己的消息,并且知道它能【发送】消息;
- 第三次:客户端 → 应答 ( ACK = 1,syn = x + 1 , ack = y + 1 )→ 服务端,服务端也确认【客户端】能【接收】消息。
那么如果只有两次握手,则【客户端】的【接收】能力并没有得到确认,不能确定这是一个可靠的连接。同时,为了实现可靠数据传输, TCP 协议的通信双方, 都必须维护一个序列号, 以标识发送出去的数据包中, 哪些是已经被对方收到的。 三次握手的过程即是通信双方相互告知序列号起始值, 并确认对方已经收到了序列号起始值的必经步骤,如果只是两次握手, 至多只有连接发起方的起始序列号能被确认, 另一方选择的序列号则得不到确认。
1.2 TCP为什么要4次挥手?
- 因为TCP是一个全双工的链接,即客户端可以向服务器传送数据,服务器也可以向客户端传送数据。当客户端【请求】断开连接时,代表客户端需要向服务器发送的数据已经完毕,服务器【确认】收到断开连接,此时进行了两次挥手。 如果和建立TCP链接时一样立刻进行第三次挥手会出现一个问题:服务器需要向客户端发送的数据此时不一定全部发送完毕,这时需要等待服务器发送完送有的数据,才能进行下面的操作。
- 当服务器也发送完所有的数据给客户端,才能进行后两次挥手。服务器向客户端发送一条断开连接的【请求】,客户端【确认】应答后,服务器立刻进入了CLOSED模式。
- 此时客户端还不能立刻进入CLOSED模式,需要等待2MSL。原因:由于客户端最后一个ACK可能会丢失,这样B就无法正常进入CLOSED状态。于是B会重传请求释放的报文,而此时A如果已经关闭了,那就收不到B的重传请求,就会导致B不能正常释放。而如果A还在等待时间内,就会收到B的重传,然后进行应答,这样B就可以进入CLOSED状态了。
1.3 停止等待协议、连续ARQ协议、选择重传
这三个协议的目的,都是为了保证了客户端和服务器的数据,能确保传送到对面。如果数据在中途丢失或者延迟,则需要重新发送,一直到对面接收到为止。
停止等待协议:
A每发送完一个报文M,就等候B对其确认;如果没收到确认,则不能继续发送,收到确认后再继续发送。缺点是一个个字节发送效率太低。
数组分组在传输过程中发生错误时有两种情况:
1、当接收方收到错误数据分组时,会直接丢弃分组。
2、如果数组分组在传输的过程中丢失。
在这两种情况下,接收方都不会发送任何信息。发送方在一定时间内没有收到确认,就认为分组丢失,然后重传该数据分组,这就叫超时重传。
停止等待ARQ协议就是通过这种确认和重传的机制,在不可靠的网络上实现可靠通信。
连续ARQ协议:
显然,每次只发送一个报文然后等待确认效率太低。连续 ARQ 协议可提高信道利用率。发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方一般采用累计确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。如果中间发生丢失包的情况,A需要回退到确认的报文重新发送。
A的发送窗口大小是根据B接受窗口设置的。也就是说A发送的报文速度不能超过B处理报文的速度。
TCP中对于超时重传时间的选择是根据平均往返时间RTT来计算的。也就是说,如果A超过一个报文平均往返时间没有收到确认,就会重新发送报文。
选择重传:
选择重传ARQ协议是指在接收方收到未按序排列的数据流时,通知发送方重传缺失的数据,而不是重传全部数据。TCP数据段首部中添加选择确认选项SACK可以实现该目的。
如图所示,假设上述分组都在发送窗口中,收到三个不连续的分组。三个分组的边界分别为:[4000,5001]、[6000,7001]、[8000,9001]。在建立TCP连接时,连接双方先商定好,在首部选项中加入“允许SACK”的选项。在之后的TCP报文段中增加SACk选项,以便接收方向发送方报告不连续的字节块的边界。
因为序号是32位,因此指明一个边界需要4个字节,说明一个字节块的边界需要8个字节。另外需要一个字节指明是SACK选项,一个字节指明这个选项的大小。TCP报文段首部选项最大为40个字节,因此最多指明4个字节块的边界信息。
TCP标准并未指明发送方应该如何响应SACK,因此大多数实现还是重传所有未被确认的数据分组。
2. TCP的流量控制
利用滑动窗口机制可以实现对发送方的流量控制。在TCP连接建立时,接收方会在确认报文段中给出自己接收窗口的大小。在每次发送确认报文时能够根据情况动态调整接收窗口的大小,并将告知发送方。如下图所示:
发送方发送序号从1开始的100字节的数据,接收方在确认报文中声明自身的接收窗口大小为300字节。之后发送方发送300字节数据,接收方在确认报文中声明自身接收窗口大小调整为50字节。发送方再发送50字节数据之后,收到接收方传来的确认报文,在该报文中声明接收窗口为0。
在接收方接收窗口为0时,发送方不再发送数据,直到接收方发送确认报文表明窗口大小发生改变。可是这个确认报文不一定能够被发送方接收到,如果一旦该确认报文丢失,双方都将处于等待中,形成死锁。为防止这种情况出现,TCP规定在收到对方接受窗口为0时,启动一个坚持定时器周期性的发送探测报文,以确定对方接收窗口为0的状态是否改变。
另外,TCP标准规定:接收方接收窗口为0时,不再接收正常数据,但是可以接收零窗口探测报文段、确认报文段、携带紧急数据的报文段。
3. TCP的拥塞控制
- 拥塞控制是指防止过多的数据注入网络中,这样可以使网络中路由器或者链路不致过载。现在通信线路的传输质量一般都很好,因传输出现差错丢弃分组的概率很小。因此,判断网络拥塞的依据就是出现了超时。
- TCP进行拥塞控制常用的算法有四种:慢启动、拥塞避免、快重传、快恢复。
3.1 慢启动
当主机开始发送数据时,如果立即将较大的发送窗口的全部数据注入网路中,那么由于不清楚网络的情况,有可能引起拥塞。比较好的方式是试探一下,即从小到大逐渐增大发送端的拥塞控制窗口数值。cwnd以指数增长的形式增长。
3.2 拥塞避免
- 当窗口值逐渐增大时,为了防止cwnd的增长导致网络拥塞,还需要一个变量——慢开始门限ssthresh。当cwnd以指数增长的形式增长到大于或等于ssthresh时,就不再采用慢启动算法,而是采用拥塞避免算法来进行拥塞控制。
- 拥塞避免算法规定:每次收到一个确认时将cwnd增加1/cwnd个SMSS。即不再是像慢启动算法那样经过一轮传输cwnd翻倍了,而是经过一轮传输增加一个SMSS。这是一种加性增长的关系。
当拥塞发生时(超时或收到重复确认),cwnd被设置为1个SMSS。ssthresh被设置为当前窗口大小的一半,但最少为 2个报文段。
3.3 快重传
- 如果个别报文段在网络中丢失,网络并没有发生拥塞,这种情况下发送方收不到确认报文,在超时之后会重传该报文。发送方误以为网络发生拥塞,错误的启动慢开始算法,降低了传输效率。
- 采用快重传算法可以让发送方尽早知道个别报文段的丢失。快重传算法要求接收方不要延时发送确认,即使收到失序的报文段也要立刻发送对已收到报文的重复确认。如下图所示:
接收方收到M1之后发送对M1的确认报文,M2报文丢失,之后接收方收到M3、M4、M5时每次都发送对M1报文的重复确认。快重传算法规定当收到三次重复确认后,发送方就认为M2报文段丢失,立即重传M2报文段。
- 快重传算法并非取消了重传机制,只是在某些情况下更早的重传丢失的报文段(如果当发送端接收到三个重复的确认ACK时,则断定分组丢失,立即重传丢失的报文段,而不必等待重传计时器超时)。
3.4 快恢复
- 当发送方连续收到接收方发来的三个重复确认时,就执行“乘法减小”算法,把慢开始门限减半,这是为了预防网络发生拥塞。
- 由于发送方现在认为网络很可能没有发生拥塞,因此现在不执行慢开始算法,而是把cwnd(拥塞窗口)值设置为慢开始门限减半后的值,然后开始执行拥塞避免算法,使拥塞窗口的线性增大。
作者:白马笑西风
链接:https://juejin.cn/post/6844903799253909512