TCP协议(2) -- TCP中内部重要的核心工作机制


在上一节中, 我们着重介绍了 TCP 协议段格式, 里面还有许多东西是第一节没有讲到的, 在这一节中, 我会根据 TCP 里的内部核心工作机制来对上一节没有详细讲的一些内容做更详细的补充.

一. 确认应答

上一节中, 我们讲到了 TCP 协议的特点, 有以下四点:

  • 有连接
  • 可靠传输
  • 面向字节流
  • 全双工

与 UDP协议不同的是, UDP 是不可靠传输, 而 TCP为可靠传输, 那么, TCP 的可靠, 可靠在哪里呢?

这里要理解的是, 可靠, 并不是字面上的可靠, 并不是说使用 TCP 发送消息, 对方就一定能收到, 可靠只是尽可能地将数据传输过去, 如果真的传输不过去, 发送方也能清楚地知道, 这一个消息接收方并没有接收到…

要想实现 TCP 中的可靠传输, TCP 中引入了一个非常重要的机制, 就是确认应答

重点: 确认应答是 TCP 实现可靠传输的最核心的机制!!


应答报文(ack)

这里给出一个场景, 来理解确认应答机制.

假如有一天中午, 我的室友在外面, 正好到了要吃饭的时间了, 这时候, 我给我的室友发送一条信息, 说: “你给我买个午饭行不行?”, 室友就会回答: “可以.”

如果我收到了室友的回答, 就代表室友接收到了我的消息(按照网络发送的场景讲, 也就是说我的消息没有丢包).
如果等了很久, 等到我室友都已经回来了, 就代表我发送的消息室友没有收到(我的消息大概率是丢包了).

在上述场景中, 室友收到了我的消息并回复了一个 “可以” , 室友发给我的 “可以” 在 TCP 中就称为[应答报文(ACK)](ACK是acknowledge(应答) 的缩写), 在上一节中的六位标志位中有涉及到.


数据包后发先至问题

在上述场景中, 我再给室友发个消息, 并得到回应

但是在第一条消息得到回应前, 我又发了消息, 可以理解为我一次发了两条消息, 则消息就变为

而第二种情况又会有一种变数, 就是消息在网络中可能会出现 “后发先至” 的情况, 在这个情况下, 收到消息的顺序是可能发生改变的… 如下图


针对上述情况, 抛出一个疑问: 为什么消息会发生 “后发先至” 的情况呢?

举个栗子:
比如我和舍友一起去走个迷宫, 假如每个人都有自己的编号, 我们是按照 1 2 3 4 号的顺序走进迷宫的, 并且我们每个人都分开走.

但是每个人走出迷宫的能力有各自的不同, 所以按照上图, 我们走出迷宫的队形就不一定是进迷宫的队形…

所以, 网络上的数据后发先至也是同理, 两个主机之间进行通讯, 所构成通讯的路径就相当于上面的迷宫一样, 可能存在多条到达目的地址的路线, 两条数据所经过的 路由器 / 交换机 的性能也可能存在差异, 有的转发数据报的速率快, 有的转发的慢, 由此, 两个数据包到达的时间就存在变数了, 这是一种非常普遍的情况…

总结: 网络数据后发先至的现象是客观存在无法避免的, 因此应答报文到达的顺序也是可能会发生变动的, 这时就要考虑如何规避这种顺序错乱…


解决数据包后发先至

上面讲了数据包会有后发先至的情况, 那么如何解决这个问题呢?

有一个非常简单的方法:
给传输的数据和应答报文都进行编号就行了

如上图, 当我们引入序号后, 就不怕数据发生错乱了, 就算数据是乱的, 我们也能根据数据的序号和应答报文的确认序号清楚应答报文是在应答哪个数据包了.

序号和确认序号

上节认识 TCP 协议段格式中, 没讲到的序号以及确认序号, 在这个场景下能更好地理解并加深印象, 所以序号和确认序号放到这边来进行理解.

如上图红框所示, 任何一条数据包(包括应答报文) 都有序号
而确认序号则只有应答报文有(在普通数据包中确认序号并没有意义)

而如上图蓝框所示, 在这六位标志位中, ack的那一位如果是 1, 表示这个报文为应答报文, 如果为 0 就不是应答报文了.

在实际上, TCP 的序号并不是像上述那样用 “第一条数据, 第二条数据…” 来表示的
TCP 的特点是面向字节流, 所以 TCP 的序号也是按照字节来进行编号的…

(上图来自书籍 《图解TCP_IP》)

按照上图来看, 第一条数据是 1000 个字节, 假设序号是从 1 开始编号的, 那么这条数据第一个字节序号就是 1, 第二个字节就是 2…
由于这 1000 个字节都是属于同一条数据包, 那么就可以表示这个数据包的序号就是 1.

假设第二条数据长度也为 1000 字节, 那么第二个数据包的第一个字节的序号就是 1001,
那么就可以表示 1001 ~ 2000 这些字节都同属于第二个数据包, 报头里面的序号就是 1001…

应答报文:
应答报文的确认序号是发送方发送的数据最后一个字节的序号 +1.

应答报文确认序号表示的含义:

  1. 这个序号之前的所有数据都已经确认收到了.
  2. 主机 A 接下来应该从 1001 这个序号开始继续发送. (也就是主机 B 向主机 A 索要 1001 的数据)

小结:
TCP 的字节序号是依次累加的, 对于后一条数据来说, 起始字节序号就是上一个数据最后一个字节的序号 +1, 每个 TCP 报头的序号就是填写这个 TCP 报文头一个字节的序号, 知道了头一个字节序号, 再根据报文长度, 就能知道每一个字节的序号…

确认应答小结:

  • TCP 特点中的 “可靠传输” 就是通过确认应答的机制来保证的.
  • 通过应答报文, 可以让发送方知道数据是否传输成功.
  • 引入序号和确认序号, 针对了多组数据进行详细的区分.

二. 超时重传

在上述讨论确认应答的时候, 只讨论了数传输顺利的情况, 但是如果数据丢包的情况呢?
这就引入了 TCP 第二个核心工作机制: 超时重传…

丢包, 也分为两种情况:

  1. 发送方发送的数据丢了.
  2. 接收方发送的 ack 丢了.
  • 在这两种情况中, 发送方都没有收到 ack, 所以这两种情况都认为是丢包了…

但是: 发送方没有收到 ack, 也分两种情况:

  1. 传输的数据走得慢, 或者 ack 走的慢, 此时 ack 已经在路上了, 但是发送方还没收到 ack.
  2. ack 丢包了

为了确定当前这次传输, 到底是丢包了, 还是 ack 走得慢, TCP 引入了一个 时间阈值

时间阈值:
在发送方发送了一个数据的那一刻, 就会等待 ack, 并同步开始计时
如果到达了时间阈值之后, 也没收到 ack, 不管 ack 是没到还是丢了, 都统一视为丢包了

为了应对丢包这种问题, TCP 引入了超时重传这个机制.

超过了一定的时间, 还没得到响应, 就要进行重新传输(重新向接收方发送一条一模一样的数据)

数据丢包

在这张图中, 主机 A 发送的数据在途中丢包了, 当过了设定的时间阈值之后还未收到 ack, 则会将此数据进行重发.

ack 丢包

主机 B 发送的 ack 在途中丢包了, 主机 A 在过了时间阈值之后还未收到 ack(已丢包), 主机 A 会将这个数据进行重发.

注意: ack 丢包会引入一个问题, 那就是发送的数据发了两份, 接收方收到了两次数据… 如果这个数据是一个扣款请求, 那么如果未对这种情况做出应对, 则会导致严重的后果(主机 B 会被扣款两次)!!!

当然, TCP肯定会对这种重复的数据进行处理

TCP 存在一个接收缓冲区这样的空间, 接收方接收到一个数据的时候, 将会对接收缓冲区里的数据按序号进行排序, 如果序号相同, 就代表这个数据在第一次传输的过程中, ack 丢包了, 发送方发送了同样的数据, 这时, 会对接收缓冲区里, 序号相同的数据进行**去重**操作, 使得接收方对数据进行读取操作时, 不会读到一模一样的数据…


总结:

  • TCP 的可靠传输是通过 确认应答 + 超时重传 来体现的
  • 确认应答是描述数据传输顺利的情况
  • 超时重传是描述传输出现问题的情况
    (两者相互配合, 共同支撑 TCP 整体的可靠性)

三. 连接管理

TCP 协议的特点里有一项是 “有连接”.
所以, 想要使用 TCP 传输数据之前, 发送方和接收方先要进行连接(Connection).

进行连接, 就需要发送方和接收方共同创建一个数据结构, 来保存对方的信息, 如下图

连接管理的核心:

  • 如何创建连接
  • 如何断开连接

建立连接(三次握手)

三次握手的意义 (1)

建立连接

建立连接首先要通信双方要各自纪录对方的信息, 彼此之间要相互认同

简单举个栗子:

比如说, 我要对一个女同学表白(1),
接下来, 女同学接受了我的表白(2),
此时, 我们两个都有了个认同: 女同学是我唯一的那个人…
但是, 想要确认男女朋友关系, 我们得互为唯一
于是, 女同学也对我表白(3),
我回应了女同学(4).
此时, 女同学那边也知道了: 我是女同学唯一的那个人…

这时候, 男女朋友的关系才正式建立, 相当于连接建立完毕.
这个过程中每次的通信, 都称为是一次 “握手”.


==但是, 标题不是说三次哦握手吗? 这里看见了四次啊?== 其实, 第二次和第三次的通信可以合并成一次发送 ![](https://img-blog.csdnimg.cn/img_convert/4e2b3ca0ae305c5bdb19b166c4cc69c5.png)

小结:
三次握手的本质, 其实是 “四次” 交互
通信双方各自向对方发起建立连接的请求, 同时向对方回应一个 ack
中间的两次交互, 必须合并成一次交互(体现了封装分用, 封装分用两次比一次成本更高)

三次握手的意义 (2)

验证 通信双方各自的发送能力和接收能力是否正常

当我想和我兄弟进行开黑的时候, 我们进行连麦, 在开黑前, 得先通过三次握手来进行测试验证

第一次交互:

当兄弟听到 “Hello?” 时,
他知道了:

  1. 我的麦克风工作正常
  2. 他的耳机工作正常

我知道了:
(无)

第二次交互:

当我听到他说话
此时, 他知道了:

  1. 我的麦克风正常

  2. 他的耳机正常

我知道了:

  1. 他的麦克风正常

  2. 我的耳机正常

但是, 这时我的兄弟他还不知道他自己的麦克风正不正常, 以及我的耳机正不正常,

所以, 就有了第三次交互:

当他听见我说话, 意味着第二次他说的话我听见了, 意味着他知道他的麦克风正常, 我的耳机正常

这时, 我们双方都知道了我们的麦克风和耳机都正常

三次握手的意义小结:

  1. 让通信双方各自建立对对方的连接
  2. 验证通信双方各自的发送能力和接收能力是否正常
  3. 握手的过程中, 双方协商一些重要的参数

三次握手总结:

客户端主动给服务器发起的建立连接请求, 称为 “SYN”(同步报文段).

在 TCP 协议段的六位标志位中的 SYN 如果为 1, 则代表这个报文段为同步报文段.
如果 ACK 为 1, 那代表这个为应答报文段
都为 1, 那么它既是同步报文段, 也是应答报文段

断开连接(四次挥手)

四次挥手和三次握手相似, 都是通信双方各自向对方发起一个断开连接的请求, 再各自给对方一个响应.

假如, 我要和上面的女同学分手

可以看出, 在上面的四次交互后, 我们两个人彻底断开了连接…
接收方和发送方也是同理, 在这四次交互后, 也彻底断开了连接.

这时候就有一个问题了, 上面提到的建立连接的过程中, 也是四次交互完成的, 其中第二次和第三次交互可以合并成一个包来发送, 那么断开连接里面的这第二个和第三个包可以合并在一起吗?
答案是: 通常情况下不能合并, 但是特殊情况下可以.

假如, 我想在同一家网上购物店铺去买东西, 如果我在同一天下单很多样东西, 那么这些东西是会装在同一包裹寄过来的(减少成本). 但是, 如果我多个物品是都间隔了一周买, 那么他们会在同一包裹里同时到我手上是不现实的…

所以, 只有在接收方对发送方断开连接的请求发送的 ack 和 接收方断开连接的请求(也就是上图的2 和 3) 发送的时机相同, 才能够合并成一个包, 如果时机不相同, 就合并不了了.


客户端与服务器断开连接的步骤可以按照下图 来理解:

这张图里的 FIN 就是断开连接请求, ACK 是回复报文

在 TCP 协议段中的六位标志位中的 FIN 就为断开连接请求, 如果这位是 1, 就代表这个报文是断开连接请求的报文.

FIN 的发起, 并不是由内核来控制的, 而是在应用层, 应用程序调用 socket 的 close 方法, 或者是应用程序的进程退出, 才会触发 FIN…
而 ACK 是由内核控制的, 在收到 FIN 请求的一瞬间内核就会立即返回一个 ACK.
接收方返回一个 ACK(第二次交互) 之后, 可能会有一个时间差, 具体取决于应用程序的代码是怎么实现的, 第二次交互完成之后, 第二次交互发送的 ACK 和第三次交互发送的 FIN 请求中间是否会有间隔, 间隔长还是短, 取决于应用程序的代码实现(收到 FIN 请求是立即调用 close 还是先干点别的事再调用 close)


在三次握手和四次挥手的过程中, 也是存在超时重传的.

假如, 四次挥手的过程中, 最后一个 ACK 丢包了, 站在服务器的视角看, 服务器并不知道是客户端发的 ACK 丢了, 还是自己发的 FIN 请求丢了, 这时会统一认为是 FIN 丢了, 进行重传操作.

服务器要重传一个 FIN, 客户端需要有能力对这个 FIN 请求进行响应, 如果只是客户端第四次发送的 ACK 丢包了, 那么如果当时已经彻底将连接释放了, 那么客户端就收不到重传的 FIN 请求了…
于是, TCP 引入了一个状态: TIME_WAIT. 在第三次交互过程后, 客户端进入 TIME_WAIT 状态, 保留了一定的时间, 就是为了能够处理最后一个 ACK 丢包的情况, 这是能够收到 重传的 FIN 响应, 再次给服务器发送 ACK 响应…


总结:
TCP 作为有连接的协议, 就需要建立连接和断开连接
其中建立连接的过程是 三次握手
断开连接的过程是 四次挥手

这三个工作机制是 TCP 协议中非常重要的三个机制, 所以花了大量的篇幅去讲解, 后面其他的工作机制会在下一篇进行讲解.

AIT. 在第三次交互过程后, 客户端进入 TIME_WAIT 状态, 保留了一定的时间, 就是为了能够处理最后一个 ACK 丢包的情况, 这是能够收到 重传的 FIN 响应, 再次给服务器发送 ACK 响应…

[外链图片转存中…(img-Vxx1RHIE-1676690797347)]


总结:
TCP 作为有连接的协议, 就需要建立连接和断开连接
其中建立连接的过程是 三次握手
断开连接的过程是 四次挥手

这三个工作机制是 TCP 协议中非常重要的三个机制, 所以花了大量的篇幅去讲解, 后面其他的工作机制会在下一篇进行讲解.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值