简谈网络协议那些事

前言

通信协议是两个节点为协同工作、交换信息而约定的规则,包括字节序、字段类型、压缩或加密方法等。常见的前端协议如 TCP、UDP、HTTP、SIP 等,都包含流程规范(如信令流程)和编码规范(即数据打包/解包规则)。 编码规范也称作序列化或编解码,不仅用于通信,也常用于存储场景,比如将内存中的对象保存到磁盘。

本文通过一个个简单逐步演进的示例,展示一个协议从简单到完善的设计过程,帮助你理解编码协议。


1. 紧凑模式

一开始,我们定义一个简单的用户基本信息结构:

c 复制代码
struct userbase {
    unsigned short cmd;  // 1-get, 2-set
    unsigned char gender; // 1-man, 2-woman
    char name[8];        // 定长姓名
}

这种方式几乎无需编码是一种row数据,数据直接从内存拷贝、调整字节序后发送。接收方也能正确解析。

其编码格式如下(每格一字节):

scss 复制代码
| cmd (2) | gender (1) | name (8) |

这就是"紧凑模式":除了原始数据,没有额外信息。在早期资源紧张的时代很常见,你想扩展或者做点其他事情几乎不可能,你被限制在仅有的空间内,位置不能移,大小不能动。


2. 可扩展性

当我们需要增加一个新字段时,问题出现了:接收方无法正常读取,协议无法正常通信。

于是早期引入版本号概念:

c 复制代码
struct userbase {
    unsigned short version;
    unsigned short cmd;
    unsigned char gender;
    unsigned int birthday;
    char name[8];
}

这样通过版本号区分新旧结构,实现了基本扩展能力。

但是弊端也是看得见的,协议版本无数多,你要维护大量的版本分支,随着版本迭代的的这种难度大到已经足以让大家放弃。重新寻找更合适的方案也就迫在眉睫。


3. 更灵活的扩展性

但仅靠版本号管理,随着版本增多,就如上面说的代码中会出现大量分支判断,难以维护。 我们为每个字段增加一个标签(tag),形成类似键值对的结构:

复制代码
| version | cmd | gender | birthday | name |

虽然增加了冗余,但换来了更好的灵活性和可读性。


4. 变长字段支持

现实中很多字段是变长的,比如姓名可能超过8字节。固定长度既浪费也不灵活。

这里可以借鉴 ASN.1 的 BER 编码方式,使用 TLV 格式:

css 复制代码
[Tag][Length][Value]

例如用户信息可编码为:

复制代码
| tag_cmd | len_cmd | cmd | tag_gender | len_gender | gender | tag_name | len_name | name... |

TLV具备了很好可扩展性,很简单易学。同时也具备了缺点,因为其增加了2个额外的冗余信息,tag 和len,特别是如果协议大部分是基本数据类型int ,short, byte. 会浪费几倍存储空间。另外Value具体是什么含义,需要通信双方事先得到描述文档,即TLV不具备结构化和自解释特性。


5. 加入自解释性

为了不依赖外部文档就能理解数据含义,我们在 TLV 基础上增加类型信息,形成 TTV

css 复制代码
[Tag][Type][Value]

其中定义了基本类型(如 int、short)可省略 Len,因为长度是已知固定的。

我们定义一套类型值:

go 复制代码
1: int16, 2: int32, 3: string, ...

编码后数据类似:

复制代码
| tag_cmd | type_cmd | cmd | tag_name | type_name | len_name | name... |

这样即使没有协议文档,也能解析出字段类型和含义。


6. 跨语言支持

当多语言协作时(如 Java/PHP 不支持无符号类型),需统一类型系统,避免兼容问题。

我们定义一套跨语言通用类型(即:交集类型),约束使用范围,保证各语言解析一致性。


7. 代码自动化

手动编解码枯燥易错,我们引入 IDL 来描述协议,通过工具自动生成不同语言的代码:

复制代码
gen_cpp sample.idl  → sample.cpp, sample.h
gen_java sample.idl → sample.java

提升效率,减少错误。


总结

从上文的介绍大致可以了解到协议设计是一个逐步完善的过程:

  • 紧凑模式 出发,逐步考虑扩展性
  • 引入标签类型实现自解释
  • 通过统一类型系统支持跨语言
  • 借助 IDL 和代码生成提升开发效率
  • 最终形成支持压缩、可选字段等特性的成熟协议

最后这篇文章大部分的知识来源互联网,在自己理解的基础上进行编写没有很生涩难懂的技术细节,本人知识的广度不足以支撑更多,如大佬略过即可。

相关推荐
helloworddm6 小时前
Orleans 自定义二进制协议在 TCP 上层实现的完整过程
java·网络协议·tcp/ip
清山博客10 小时前
Springboot 局域网部署https解除安全警告
网络协议·安全·https
小楊不秃头11 小时前
网络原理:数据链路层、NAT与网页加载
网络·网络协议
我叫汪枫12 小时前
《HTTP 的进化史:从 1.0 到 3.0 的飞跃》
网络·网络协议·http
我叫汪枫12 小时前
《拆解一封网络信:HTTP 报文详解》
网络·网络协议·http
kevien_G113 小时前
TCP与UDP深度理解
网络协议·tcp/ip·udp
周杰伦_Jay13 小时前
【计算机网络核心】TCP/IP模型与网页解析全流程详解
网络·网络协议·tcp/ip·计算机网络·算法·架构·1024程序员节
心无旁骛~14 小时前
Socket和Websocket编程的区别
网络·websocket·网络协议
tuokuac14 小时前
如何判断“IP+端口“通不通
网络·网络协议·tcp/ip