协议定义参考
大端小端
- 首先,为什么会有小端字节序?
- 答案是,计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的。所以,计算机的内部处理都是小端字节序。
- 但是,人类还是习惯读写大端字节序。所以,除了计算机的内部处理,其他的场合几乎都是大端字节序,比如网络传输和文件储存。
计算机处理字节序的时候,不知道什么是高位字节,什么是低位字节。它只知道按顺序读取字节,先读第一个字节,再读第二个字节。
如果是大端字节序,先读到的就是高位字节,后读到的就是低位字节。小端字节序正好相反。
理解这一点,才能理解计算机如何处理字节序。
uint16_t 大端 ?小端
为什么大端格式被认为是网络通讯中的“标准”?
计算机历史原因
- 早期计算机(如 IBM 360)采用大端存储,许多协议的设计基于这些系统,因此延续了大端的传统。
符合人类阅读习惯
- 大端格式与我们日常习惯一致:
- 十进制数:1234,我们从左到右读 (千 → 百 → 十 → 个)
- 大端存储:高字节在低地址,例如 0x1234 存储为 [0x12] [0x34]
- 小端存储:低字节在低地址,例如 0x1234 存储为 [0x34] [0x12](x86架构)
- 因为大端符合阅读顺序,所以在早期标准化时更容易被接受。
- 大端格式与我们日常习惯一致:
网络通信需要一致性
- 在网络通信中,不同设备可能有不同的 CPU 架构(大端/小端),但如果所有协议都用大端格式,就不需要设备之间转换字节序,解析更简单。例如:
- IP 地址、端口号、协议字段都是大端格式,这样不同架构的设备都能正确解析数据包。
- 在网络通信中,不同设备可能有不同的 CPU 架构(大端/小端),但如果所有协议都用大端格式,就不需要设备之间转换字节序,解析更简单。例如:
兼容 TCP/IP
- TCP/IP 是互联网的基础,所有网络设备都必须兼容它,而 TCP/IP 强制使用大端格式。
- 如果你的自定义协议与 TCP/IP 交互(如嵌入到 UDP/TCP 数据包中),使用大端格式会更方便。
为什么 x86 是小端?
- 虽然大端有标准化优势,但 x86 架构(Intel 处理器)选择了小端格式,因为:
- 方便按字节扩展(低地址对齐低位操作更高效)
- 早期设计者认为小端格式的内存寻址更高效
- 但这导致了小端和大端设备的兼容问题,因此 htonl() / ntohl() 这些转换函数就出现了。
- 虽然大端有标准化优势,但 x86 架构(Intel 处理器)选择了小端格式,因为:
结论
- 大端格式之所以被标准化,不只是因为 TCP/IP,而是因为历史、兼容性和可读性。
- TCP/IP 选择大端是为了让不同 CPU 架构统一字节序,减少跨平台解析问题。
- 如果你的协议和 TCP/IP 交互,推荐使用大端格式(网络字节序),否则可能需要额外转换。
总结
- 跨平台使用大端
- 不跨平台使用大端或小端
- ps: 整型最好使用大端的以便兼容网络协议
len 数据长度 ?总长度
协议中的“长度”字段:数据长度 vs. 整个协议长度?
- 在自定义协议或标准协议(如 TCP/IP)中,“长度”字段可能表示两种不同的含义:
- 仅表示数据部分的长度(Payload Length)
- 表示整个协议数据包的长度(Total Length)
- 每种方式都有不同的 优缺点,而标准协议(如 TCP/IP)采用的方式有其特定原因。
- 在自定义协议或标准协议(如 TCP/IP)中,“长度”字段可能表示两种不同的含义:
方式 1:长度字段表示数据长度(Payload Length)
- 即 长度字段表示有效负载(payload)的字节数,不包括协议头部。
- 优点:
- 减少计算开销:解析时,协议头的大小通常是固定的,只需要读取长度字段来确定数据的大小。
- 通用性强:在封装嵌套协议(如 TCP 之上的应用层协议)时,较容易处理数据部分。
- 缺点:
- 难以直接确定总数据包大小:接收端必须知道协议头部的固定长度或额外字段来计算整个包的大小。
- 额外的计算步骤:如果需要总长度,需要额外加上头部长度。
- 采用此方式的协议示例
- TCP (Transmission Control Protocol)
- TCP 头部中没有“总长度”字段,只有Data Offset(即 TCP 头的大小),数据长度是 “IP总长度 - IP头部长度 - TCP头部长度” 计算得到。
- 为什么? TCP 是面向流(stream)的协议,不关心单个数据包的完整性,而是基于字节流传输,因此只需要知道数据部分的长度。
- TCP (Transmission Control Protocol)
方式 2:长度字段表示整个协议包的长度(Total Length)
- 即 长度字段表示“协议头 + 数据”的总大小。
- 优点:
- 更容易确定数据包的完整性:接收方可以直接知道数据包的总大小,方便验证是否接收完整。
- 方便调试和解析:在网络分析工具(如 Wireshark)中,直接显示整个数据包的大小,容易排查问题。
- 减少接收端计算量:接收端无需额外计算总长度,只需读取该字段。
- 缺点:
- 需要额外存储头部长度信息:在某些情况下,头部可能是变长的,仍然需要字段指示头部长度(如 IP 头)。
- 嵌套协议处理较复杂:如果在协议之上封装其他协议,需要拆分出各层的头部信息。
- 采用此方式的协议示例
- IP (Internet Protocol)
- IPv4 头部中的 Total Length 字段表示 整个 IP 数据包的大小(IP 头部 + 负载)。
- 为什么? IP 是无连接的,每个 IP 数据包是独立传输的,接收方需要知道整个数据包的大小,确保正确接收和组装。
结论
- 网络层(如 IP)通常使用“总长度”,因为它需要完整的数据包大小来正确解析和转发数据。
- 传输层(如 TCP)通常使用“数据长度”,因为它是基于流的协议,不需要关心头部之外的长度。
- 应用层协议(如 HTTP、TLS)可以选择任何方式,但通常会提供“内容长度”字段来表示实际数据长度。
- 如果你在设计自定义协议:
- 如果是基于数据包(Datagram)传输(如 UDP)👉 推荐使用“总长度”
- 如果是基于流(Stream)传输(如 TCP 上的应用层协议)👉 推荐使用“数据长度”