Linux 序列化与反序列化原理及自定义协议实现
在 Linux 网络编程和系统开发中,序列化与反序列化几乎是绕不开的话题。无论是进程间通信,还是基于 Socket 的网络传输,数据最终都需要以字节流的形式在系统中流转。很多初学者在实际开发中往往只会'用协议',却并不清楚协议是如何设计的、数据又是如何被序列化和还原的。今天我们将围绕 Linux 环境下的序列化与反序列化,从基本原理入手,逐步分析自定义协议的设计思路与实现方式。
一、序列化与反序列化
在深入概念之前,先回顾一下直接传递结构体的局限性。比如通过网络完成一个简单的计算器,客户端和服务端需要约定好传输的结构体内容。虽然这种方式效率极高,CPU 占用低,但存在明显隐患:
- 内存对齐问题:不同编译器或操作系统对结构体的填充规则可能不同,导致接收方读取乱码。
- 大小端问题:如果客户端是小端机器,服务端是大端机器,数据读取顺序会相反。
- 适配性问题:跨语言通信(如 C++ 客户端与 Java 服务端)时,很难完美模拟对方内存中的物理布局。
为了解决这些问题,我们引入序列化与反序列化。

将数据由结构体转化为字符串(多变一),方便网络发送的过程叫序列化;反之,将字符串按规则还原为结构体(一变多),方便上层处理的过程叫反序列化。
相较于直接传递结构体,序列化的优势在于:
- 方便网络发送:统一格式,减少传输开销。
- 可扩展性与维护性:支持新旧版本共存。例如未来增加字段(如头像、性别),接收方只需忽略未知字段即可,无需强制同步修改结构体定义。
自定义协议的本质,就是制定双方都能识别的、符合业务需求的数据结构(struct 或 class)。
二、重新理解 IO 函数与 TCP 全双工
有了基础认知后,我们需要重新审视 read, write, recv, send 这些 IO 函数。
在使用 Socket API 时,内核会为每个连接形成发送缓冲区和接收缓冲区。IO 函数的本质是拷贝:发送是将用户数据拷贝到内核缓冲区,读取是从内核缓冲区拷贝到用户空间。至于何时发送、发多少,则由 TCP 协议自行决定,这也是它被称为'传输控制协议'的原因。
这里常遇到两个经典问题:半包和粘包。
- 半包问题:发送的数据量超过接收方缓冲区容量,导致一次只能读取部分数据。
- 粘包问题:发送方多次发送小数据,接收方一次性读出了多组数据,无法区分边界。
解决这两个问题的关键在于自定义协议。TCP 协议像快递员,只负责把包裹安全送达,不关心包裹里是什么;而自定义协议规定了包裹里的具体内容格式,确保收发双方能正确解析。
关于 TCP 全双工特性,核心原因在于每端都有独立的发送和接收缓冲区,互不影响。每个通过 socket 创建的文件描述符,内核都会分配一套独立的缓冲区,确保多路复用时的隔离性。
三、网络版计算器实战
3.1 约定方案
为了实现一个可靠的网络计算器,我们需要约定通信规则。这里采用第二种方案:定义结构体标识交互信息,发送前序列化为字符串,接收后反序列化回结构体。
3.2 代码实现
为了简化 Socket 操作,我们封装了一套 Socket 接口类,采用模板方法模式设计。


