JavaEE 网络原理——TCP的工作机制(初篇 包含 UDP 协议的再次阐述)

文章目录

  • 一、再次简述 UDP 协议
  • 二、再次简述 TCP 协议
  • 三、描述部分 TCP 内部的工作机制
    • 1. 确认应答
    • 2. 超时重传

前提:
在前面的文章中,我向大家分别简单介绍了 TCP 协议和 UDP 包装一个数据形成数据报发送信息。
除此之外,还通过代码编写了 UDP 和 TCP 协议下的 客户端和服务代码。
这篇文章就是对 UDP 协议和 TCP 协议进行更进一步的解释。

一、再次简述 UDP 协议

UDP 协议特点:
无连接,不可靠传输,面向数据报,全双工。
UDP 报文 = UDP 报头 + UDP 载荷
JavaEE 网络原理——TCP的工作机制(初篇 包含 UDP 协议的再次阐述)_第1张图片
如上图所示,这就是一个有关 UDP 的报文传输信息的格式

有关 UDP 的报头:

  • UDP报头的创建
    UDP 会将载荷数据通过 UDP socket,也就是 send 方法拿来的数据基础上在前面拼接上几个字节的报头。
    注:这里的拼接相当于字符串拼接(是二进制的形式)

  • UDP 报头中的属性
    UDP 报头中包含了一些特定的属性,携带了一些比较重要的信息。
    不同的协议,功能不同,报头中带有的属性信息就不同。
    对于 UDP 来讲,报头一共就是有 8 个字节,分成 4 个部分(每个部分 2 个字节)

我们知道,在一次网络通信中,需要涉及到五元组,分别为:
源 IP,目的 IP,源端口,目的端口,协议类型。
我们要知道,在 UDP 报头中存储的UDP报文长度,其中指定了 UDP 整个数据报的长度就是 2 个字节对于 2 个字节所能表示的范围是,0 -> 65535,换算成单位也就是 64 kb。

也就是说,根据报头中的 UDP 报文长度 的规定,我们可以知道,对于一个 UDP 数据报,报头+载荷 的长度是不能超过 64kb 的。

一个小问题:
如果这里的应用层数据报超过了 64 kb怎么办?

  1. 需要在应用层通过代码的方式针对应用层的数据报进行手动拆包,拆成多个包通过 UDP 的数据报形式传输。(也就是说,多次 send)
  2. 不使用 UDP 改为 TCP 传输(TCP没有限制)

简单解释校验和
检验和的作用其实很明确,就是验证传输的数据是否正确。
我们都知道,网络传输,实际上就是光信号 / 电信号的传输。这些有可能会受到一些物理环境的影响,导致传输的数据发生细微的变化,从而产生错误。
例如:1111000 -> 1111001

二、再次简述 TCP 协议

TCP 全名叫做,控制传输协议。和名字所表示的含义一样,就是要对数据进行一个详细的控制。

TCP 协议段落格式和 UDP 的有很大的差别,下面,我通过一个简单的图示来解释 TCP 协议中的部分关键信息

TCP 报文 = TCP报头 + TCP 载荷

JavaEE 网络原理——TCP的工作机制(初篇 包含 UDP 协议的再次阐述)_第2张图片
如上图所示,就是一个 TCP 数据报中所包含的内容,下面进行部分解释

  • 16位源端口号 / 16位目的端口号:与 UDP 一样,都是表示端口号。

  • 32 位序号 / 32 位确认号后面解释。。

  • 4 位首部长度:一个 TCP 报头,长度可变,不像 UDP 一样固定 8 个字节。
    所以,首部长度就是描述了 TCP 报头具体多长。(需要注意的是,此处的首部长度是 4bit 位)

  • 保留 6 位:顾名思义,就是先保留一个空位,防止之后有其他使用。其实,也就是为了以后便于扩展来考虑的。

  • 6 位标志符:

  • URG :紧急指针是否有效。
  • ACK : 确认号是否有效。
  • PSH : 提示接受端引用程序立即从 TCP 缓冲区将数据读走。
  • RST : 要求对方重新建立连接。(我们将携带 RST 表示的称之为 复位报文段)
  • SYN : 请求建立连接。(我们将携带 SYN 的表示称之为 同步报文段)
  • FIN : 通知对方,本段要关闭了。(我们将携带 RST 标识的称之为 报复性文段)

详细的功能,本人会在后面的文章中进行简单分析。

  • 16 位窗口大小后面解释。。
  • 16 位校验和:与 UDP 中的校验和同理。
  • 选项:在这里相当于对这个 TCP 报文的一些属性进行解释说明,可以先忽略。

不难发现,上面的内容我们能理解的东西非常有限,所以,要更好的理解 TCP 数据报,就需要进一步了解 TCP 后续的工作机制。

三、描述部分 TCP 内部的工作机制

TCP 是一个很复杂的协议,其中有许多的机制。当前我们主要讨论比较重要的 10 个核心机制

1. 确认应答

这里的确认应答,是实现可靠传输的最核心机制。
这里的 “可靠” 并不是说一定 100% 将信息发送给对方,而是说尽力而为,如果实在传输不过去,至少还可以知道

这里为了方便解释,在这里本人就引入一个简单的例子。如下:
这个例子里有两个人,我(本人),外卖店老板。

JavaEE 网络原理——TCP的工作机制(初篇 包含 UDP 协议的再次阐述)_第3张图片
如上图所示,当我收到 “好的,没问题” 此时,我就会知道,本人发的消息此时已将顺利的被外卖店老板收到了。(也就是说此时的信息没有丢包)
所以说,这里的 “好的,没问题” 就被称为应答报文

但是,在这里,如果我隔了好大一会店家还没有给我回消息,这就说明发的消息大概率是凉了。。
在生活中,上面的情形和打电话非常类似。

这里考虑更复杂的一些情况
如图:

JavaEE 网络原理——TCP的工作机制(初篇 包含 UDP 协议的再次阐述)_第4张图片
需要注意的是,这里是我 连续发送两个消息
要注意的是,在网络上存在一种情况叫 “后发先至”。在这个情况下,收到消息的顺序可能会发生变化。也就是说会成下面这样,如图:
JavaEE 网络原理——TCP的工作机制(初篇 包含 UDP 协议的再次阐述)_第5张图片

此时,由于 “后发先至” 的问题,我先收到的信息是 “滚,不行”,后收到的是 “好的,没问题”。

简单解释 “后发先至”
所谓后发先至,可以简单理解为结婚时的车队。在两家较长的路上,车队之间必然会出现后面的个别车辆跑过头车走到前面去。
在网络中也是如此,两个主机之间存在多条路线,数据报1 和 数据报2 所走的路线可能不同,在不同的路由器 / 交换机上的转发速率也不相同。所以,这两个数据报到达的顺序,就更存在变数了。

当然上面的 “后发先至” 问题也是有解决办法的。就是将 传输的数据 和 应答报文 都进行编号,就可以了。
如图:
JavaEE 网络原理——TCP的工作机制(初篇 包含 UDP 协议的再次阐述)_第6张图片
如图,在这里引入序号之后,此时就不怕顺序错乱了。

解释序列号在 TCP 协议中的位置

JavaEE 网络原理——TCP的工作机制(初篇 包含 UDP 协议的再次阐述)_第7张图片

32 位序号, 在这里就是来标记数据的 (包括应答报文)
32 位确认序号, 这里的确认序号,只有 应答报文有。(普通报文中确认序号字段里的值毫无意义)
对于,如何确定这个报文是应答报文,这里取决于上图中划红线的 ACK 标志符。
此处 ACK 这个标志位如果是 1 ,就表示是 应答报文。 如果是 0,就表示不是应答报文

简单说明 TCP 序号的编号方式
我们知道,TCP 是面向字节流的。所以 TCP 的序号也是按照字节的方式来进行编号的!
例如,这里有一段数据 是 1000 个字节
呢么,假设此时是从 1 开始编号,此时的第一个字节序号就是 1 。第二个字节序号就是 2 。。。以此类推。
但是需要注意的是,这 1000 个字节都是属于一个 TCP 报文。报头中记录的只是当前第一个字节的序号。所以,此处的报头的序号就是 1。
如果接下来再发第二条数据,此时 TCP 数据报的第一个字节序号就是 1001 。如果长度是 1000,呢么到最后时的字节序号就是 2000.
所以,此时这里的 TCP 报头就是 1001.

所以,综上所述,TCP 知道了头一个字节的序号在根据 TCP 报文长度就很容易知道每个字节的序号。

简单总结:
TCP 的可靠传输能力,最主要就是通过确认应答机制来保证的。所以,通过应答报文,就可以让发送方清楚地知道传输是否成功。进一步引入的序号和确认序号,可以对多组数据进行详细的区分。

2. 超时重传

在前面的确认应答的讨论中,我们所讨论的,只是 顺利传输 这样的情况。呢么,如果出现丢包的情况呢?

所谓的丢包,基本涉及到两种情况
1.发送的数据丢失
2.返回的 ack 丢失

对于上面的两种情况,在发送方看来就是没有收到 ack 并不能做出任何区分。 也就是说会一视同仁,都认为是丢包。

所谓 “丢包” 其实是一个概率性事件。在通常情况下丢包的概率是比较小的。因此,如果重新发送一下这个数据报,还是有很大概率传输成功。

所以,根据上述的情况。TCP 就引入了重传机制。 也就是在丢包的时候再发送一次相同的数据。

但是,在重传前,我们要考虑一个问题:
当前的这次传输,到底是丢包,还是 ack 传输较慢在路上。

同样,根据上述的情况,TCP 直接引入了一个时间阈值。也就是说,当发送方发出一个数据后,就会等待 ACK ,此时开始计时。如果在时间阈值内没有收到 ACK ,无论此时是否在路上,还是丢了,都视为丢包了!

视为丢包的情况有下面两种,如图:
JavaEE 网络原理——TCP的工作机制(初篇 包含 UDP 协议的再次阐述)_第8张图片
注意上面的第二张图,对于主机 B 而言,1~1000 就接受了两次。这其实是一个很严重的问题,如果说此时发送的是一个 支付请求 呢?
在 TCP 中其实也考虑到了这一点,有着一个特殊处理——去重

在 TCP 中,存在着一个 “接收缓冲区” 这样的存储空间。(也就是操作方操作系统内核中的一段内存)
每个 TCP 的 socket 对象都有一个接收缓冲区。
在上面的图中,主机 B 接收到 主机 A 发来的数据。其实是 B 的网卡获取到数据,然后将个数据存放到 B 对应的 socket 的接收缓冲区中。(这里其实可以想象成存入到了一个阻塞队列中)
根据数据的序号,TCP 很容易识别当前的接收缓冲区中的两个数据是否重复。
如果重复,则就会将后面的这份数据丢弃。保证程序调用的 read 读取到的数据一定不重复!

对于上面提到的 “接收缓冲区” 严格的来讲,这里可以理解成一个 优先级队列 或者 有序队列。
因为在前面我们提到过,网络传输的数据可能会后发先至。这里使用这个队列就是对收到的数据进行排序,确保 read 读取到的数据是有序的。

到这里,我们描述了 确认应答 传输顺利的情况。超时重传 传输故障的情况。两者之间的配合,共同支撑起 TCP 的可靠性!

码子不易,您小小的点赞是对我最大的鼓励!!!

你可能感兴趣的:(JavaEE,网络,java-ee,tcp/ip)