简谈网络协议那些事

前言

通信协议是两个节点为协同工作、交换信息而约定的规则,包括字节序、字段类型、压缩或加密方法等。常见的前端协议如 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 和代码生成提升开发效率
  • 最终形成支持压缩、可选字段等特性的成熟协议

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

相关推荐
zxanz115 分钟前
SSL 证书申请下来快吗?
网络·网络协议·ssl
网络小江3 小时前
上网第五十二课:加密隧道与拨号专网——安全接入的通俗解释
网络协议
盛世宏博智慧档案6 小时前
TCP以太网温湿度传感器:从RS485痛点突围到工业组网实战
网络·网络协议·tcp/ip
wdfk_prog6 小时前
LWIP教程 13:从 `dhcp_start()` 到 `DHCP_STATE_BOUND`——DHCPv4、ACD、Timer 与真实抓包
运维·网络协议
zoyation7 小时前
jrp 1.2.0 重磅发布|P2P打洞直连 · 端口自动分配 · 一端口多服务,这次全齐了!
网络·网络协议·p2p
盛世宏博智慧档案8 小时前
IP 温湿度设备频繁断连?TCP 心跳保活机制调试与参数设置指南
网络·网络协议·tcp/ip·php·传感器·温湿度
网络小江9 小时前
上网第五十六课:第八周复盘——实战与安全快问快答
网络协议
青衫客369 小时前
从网卡到 Socket:再探 Linux 网络协议栈与 iptables 数据包处理流程
linux·网络·网络协议
zxanz110 小时前
SSL 证书申请中,服务器验证跟 DNS 解析验证有什么区别?
服务器·网络协议·ssl
TechWayfarer19 小时前
精度极限的突破:AI与IPv6如何重塑高精度IP查询
人工智能·网络协议·tcp/ip·transformer