目录
[1. UDP 协议](#1. UDP 协议)
[1.1 协议特点](#1.1 协议特点)
[1.2 协议报文格式](#1.2 协议报文格式)
[1.2.1 UDP 长度](#1.2.1 UDP 长度)
[1.2.2 校验和](#1.2.2 校验和)
1. UDP 协议
在进行网络编程时, 我们已经对 UDP 协议进行了简单了解. 并且应用层的很多操作, 需要调用传输层的提供的接口, 基于 socket api 来进行完成的.
1.1 协议特点
UDP 协议具有以下特点:
- 面向数据报
- 不可靠传输
- 全双工
- 无连接
在进行网络编程使用 UDP socket api 进行数据传输时, 我们就对其中的一些特点有了一些感受:
面向数据报体现于: 每发送(send)或接收(receive)一次消息, 都是一个完整的 udp 报文.
全双工体现在: 不论是客户端还是服务器, 都可以进行 send 和 receive 操作.
无连接体现在: UDP 报文直接发送, 对端就可以接收, 并没有像 TCP 那样 accept 的过程.
不可靠传输, 是指消息发出后, 就不管了(不管数据是否成功发送到对端). 这一点并没有在编程时具体体现, 接下来我们通过 udp 协议的报文格式来了解这一特点.
1.2 协议报文格式
UDP 数据报由两个部分组成:
- UDP 报头
- 载荷(完整的应用层数据包)
其中, 载荷就是完整的应用层数据报.
报头, 大小为 64 个 bit 位, 8 个字节, 由以下四个部分组成:
- 源端口号
- 目的端口号
- 报文长度
- 校验和
其中, 每个部分都占了 2 个字节.
至于源端口号和目的端口号的作用, 这里就不再赘述了, 主要聊一聊其中的 报文长度 和 检验和.
1.2.1 UDP 长度
UDP 报文格式中的长度这一部分, 表示的是整个 UDP 数据报的长度, 即报头 + 载荷的长度.
由于大小为 2 字节, 也就是 16 个 bit 位, 所以 UDP 数据包长度的范围为 0~2^16-1, 也就是 0~65535, 所以一个 UDP 数据报最大只能为 65535 个字节, 也就是 64kb.
由于报头部分的大小是固定的, 8 字节, 这相对于 64kb(65535 字节) 来说是可以忽略不计的.
所以, 当我们面试表述 UDP 报文大小的上限时, 既可以说 64kb 是整个 UDP 数据包的上限, 也可以说是 UDP 携带的载荷的上限.
64 kb, 在设计 UDP 的那个年代, 确实是一个充裕的大小, 但是放到 2024 年的今天, 就是一个非常小的数字了, 随随便便一张照片就可能是几个 MB.
所以要发送数据的大小超出 UDP 数据包可以承载的大小的情况是经常发生的, 而当发送的大小一旦超过了 64kb, UDP 数据包就会被直接截断, 对方拿到的数据就会出错, 那么如何解决这个问题呢?? 有两个方案可以选择:
- 方案一: 在应用层代码上进行拆包操作, 把一个大的应用层数据报拆成很多小的数据包, 再使用多个 UDP 数据包进行传输.
- 方案二: 使用 TCP 协议, 使用 TCP 数据包进行传输(TCP 没有长度的限制).
方案一, 是一个工作量比较大的方法, 需要对代码进行大量的修改, 写大量的逻辑来实现分包组包的工作, 并且一不小心可能就写出了隐藏 bug, 效率太低.
所以, 在工作中, 我们会优先选择简单且不易出错的方案来解决问题, 即采用方案二, 使用 TCP 来解决 UDP 数据包大小限制的问题.
可能大家心中有一个疑问: 为啥不直接修改 UDP 的报文格式, 直接把 UDP 本身改的大一点呢??
其实, 要修改 UDP 很容易, 但是难就难在, UDP 已经内置在各种的操作系统中, 一旦发生变动, 就会造成很大的影响, 比如通信双方, 一方进行了修改, 另一方没有进行修改, 那么 UDP 数据包就会传输失败, 导致不能使用 UDP 进行通信. 并且, 修改也会牵扯到系统兼容性的问题, 所以, 通信双方谁先改, 谁可能就会出现问题.
所以 UDP 可以说是积重难返了, 目前也只能这样子~~
1.2.2 校验和
UDP 报文中的校验和, 作用是为了验证传输的数据是否发生修改.
注意, 这里的校验和与 HTTPS 中数字签名的校验和不同, HTTPS 数字签名的校验和是为了反正数据被黑客篡改(防人), 但是这里 UDP 的校验和, 与安全性无关, 而是防止传输过程中出现"比特翻转".
比特翻转, 就是在传输的过程中, 由于外界的影响, 导致数据中的某些 bit 位0变1, 1变0.
(数据都是以光电信号或者电磁波的形式进行传输的, 很容易收到外界的干扰)
验证的过程如下:
- 发送之前, 会把整个数据报中的数据代入, 计算得出一个校验和, 并将数据和校验和一同发给对端.
- 对端收到数据后, 根据收到的数据, 再次计算校验和, 将两个校验和进行对比, 如果校验和不一致, 说明有数据发生了比特翻转, 直接丢弃.
这里 UDP 采用的 CRC(循环冗余校验) 的方式来计算校验和:
- CRC: 把 UDP 报文中的每个字节(除了校验和位置外), 都当做整数, 进行累加, 即使溢出也没关系, 继续加, 最终得到的数据就是 CRC 校验和.
所以, 如果在传输过程中, 有 bit 位发生了翻转, 那么两次计算得到的校验和也会不同.
我们可以认为, 如果两个数据是相同的数据, 那么计算得到的校验和一定是相同的.
但是, 当两次计算的校验和相同时, 两个数据不一定是相同的.
因为, 这其中可能存在变数, 例如: CRC 是累加的方式计算的校验和, 如果第一个字节发生的翻转, 使得值变小了, 而第二个字节发生的偏转又使值变大了, 而恰恰这两次偏转各自的差值又相互抵消, 那得到的校验和依然是相同的.
虽然, 可能出现上述情况, 但是这种情况发生的概率是非常小的(发生翻转的概率本来就小, 再加上两次偏转的影响又相互抵消, 概率就小之又小了), 所以在实际开发中, 我们可以忽略这种情况的存在.
综上, 我们可以通过 UDP 报文中的校验和这一部分, 来确定数据在传输时, 是否发生了修改~
++END++