Fork me on GitHub

TCP三次握手、四次挥手、滑动窗口、流量控制

在学习TCP协议之前,我们了解一下TCP头的报文格式

file

  • 最上层是源端口号和目标端口号
  • 接下来是包的序号和确认序号,这里的序号的作用就是维持TCP的顺序性和可靠性两种特性
  • 接下来左边部分是当前的状态位,而大名鼎鼎的三次握手和四次挥手就是基于这个状态位实现的
  • 右半部分是滑动窗口,主要是为了实现流量控制和拥塞控制

记住这个报文格式,接下来的内容都是基于这个的

三次握手

首先我们从一个两个人打电话的例子来描述一下:

  • A:哈喽,B。能听到么?
  • B:你好,A。我能听到,你能听到么?
  • A:太好了,我也能听到。我们来唠嗑么。

这个过程其实可以完美的解释三次握手的机制。我们知道,网络环境总是不安全的,只有至少经过这三次交互才能确认两方的发送和接受能力都没问题。

我们来看一下这几步分别确定了什么:

  • 第一回合:当A将要跟B通信时,它会先发出去一个包。
  • 第二回合:当B收到这个包的时候,就可以确定A的发送能力没问题,自己的接受能力没问题,它就会给A一个回复说:我收到了你的包了。
  • 第三回合:A收到B的返回信息后就可以确定自己的发送和接受都没有问题,B的接受和发送能力都没有问题。但是这个时候B还不能确定自己的发送能力,所以只有当A再次发送一个包告诉B我收到你的包了才算是完成了这个握手。

三次握手除了上方的连接之外,还会牵扯到我们文章刚开始时说的包的序号和状态位,下面这个图就是一个完整的三次握手流程
file

一开始,A和B都处于 CLOSED 状态。先是B主动监听某个端口,处于 LISTEN 状态。然后A主动发起连接 SYN,之后处于 SYN-SENT 状态。B收到发起的连接,返回 SYN,并且 ACK A的 SYN,之后处于 SYN-RCVD 状态。A收到B发送的 SYNACK 之后,发送 ACKACK,之后处于 ESTABLISHED 状态,因为它一发一收成功了。服务端收到 ACKACK 之后,处于 ESTABLISHED 状态,因为它也一发一收了

四次挥手

理解了三次握手之后再看四次挥手就比较简单了。直接看图
file
当消息传输完毕后,断开链接的过程就是这样的:

  • A:我要挂了哈,拜拜
  • B:嗯嗯,好的
  • B:我也要挂了,拜拜
  • A:嗯嗯,好的。等待2MSL如果没有异常场景就结束了

上方有一个字段MSL,它是报文在网络上的最大生存时间。那么为什么要等待2MSL才会断开链接呢?

主要是因为报文的最大生存时间是1MSL,如果A直接关闭的话,它占用的端口如果马上建立了一个新的连接,那么新的连接就有可能收到刚才B发送的迟到的报文,从而误以为是发给自己的

TCP消息的顺序性和可靠性

刚才我们已经知道了,TCP中每一个包都有一个序号,为了保证顺序性和可靠性,TCP的发送方和接受方都是需要记录这些序号相关的数据

为了记录所有发送的包,发送端需要记录以下这些场景的数据:

  • 发送了并且已经确认的
  • 发送了并且尚未确认的
  • 没有发送,但是已经等待发送的
  • 没有发送,并且暂时还不会发送的

也就是如下图这样
file

而接收端记录的内容要简单一些:

  • 接受并且确认过的
  • 还没接收,但是马上就能接收的
  • 还没接收,也没法接收的
    file

基于上面两个图,我们来研究一下顺序性和可靠性:

在发送端来看,1、2、3 已经发送并确认;4、5、6、7、8、9 都是发送了还没确认;10、11、12 是还没发出的;13、14、15 是接收方没有空间,不准备发的。

在接收端来看,1、2、3、4、5 是已经完成 ACK,但是没读取的;6、7 是等待接收的;8、9 是已经接收,但是没有 ACK 的。

发送端和接收端当前的状态如下:

  • 1、2、3 没有问题,双方达成了一致
  • 4、5 接收方说 ACK 了,但是发送方还没收到,有可能丢了,有可能在路上。
  • 6、7、8、9 肯定都发了,但是 8、9 已经到了,但是 6、7 没到,出现了乱序,缓存着但是没办法 ACK

而当发生丢包问题,或者不能保证有序性时,有这几种方式可以处理:超时重传、快速重传、SACK

TCP滑动窗口

在TCP的传输的时候,我们知道如果一个数据比较大的话就会把这个数据划分为多个包来进行发送。而为了保证消息的顺序性和可靠性的话就必须如下图所示当一个包确认后才能发送下一个包
但是这样一来就会存在一个问题,每发送一个数据包,都需要得到接收端的确认应答以后才能继续,那这个等待确认包的过程中就会很浪费时间。
file

为了应对这种场景,TCP协议就引入了滑动窗口的概念,再次拿来刚才用到的这个图
file
这里我们假设滑动窗口的大小为9不变的,也就是说这个机制可以一次性的发送一个窗口数量的包。这个时候每当收到几个应答包的时候这个窗口就会后移一个位置,假如这个时候收到了4号消息的ack,那么这个数据就成这样了
file
这样一个窗口根据消息的ack不断后移的过程就称为滑动窗口

TCP流量控制

上方说到了TCP发送方的滑动窗口,但是接收端处理数据包的能力是不同的,比如说:

  1. 如果窗口过小,发送端发送少量的数据包,接收端很快就处理了,并且还能处理更多的数据包。这样,当传输比较大的数据时需要不停地等待发送方,造成很大的延迟
  2. 如果窗口过大,发送端发送大量的数据包,而接收端处理不了这么多的数据包,这样,就会堵塞链路。如果丢弃这些本应该接收的数据包,又会触发重发机制

为了避免这种现象的发生,TCP提供了流量控制的机制,我们刚才说到的假设的滑动窗口的大小是固定的,而流量控制就是根据接收端的能力而去调整窗口的大小,这个流程是这样的:
发送端第一次以窗口大小(该窗口大小是根据链路带宽的大小来决定的)发送数据包,接收端接收这些数据包,并返回确认应答包,告诉发送端自己下次希望收到的数据包是多少(新的窗口大小),发送端收到确认应答包以后,将以该窗口大小进行发送数据包

posted @ 2020-03-09 17:36  石玉军  阅读(814)  评论(0编辑  收藏  举报