第二讲:序列化基础概念

什么是序列化?说白了就是“打包”

各位同学好,我是老张。今天咱们聊聊序列化这个基础概念。

序列化这个词,听起来挺唬人的。其实说白了,就是把内存里的数据结构——比如结构体、类、数组——转换成一段连续的字节流。我习惯把它叫做“打包”。

你想想看,在ECU(电子控制单元)内部,数据是以结构体的形式存在的。比如一个车速信号,在代码里可能是这样的:

struct VehicleSpeed {
    uint32_t value;      // 车速值,单位0.01km/h
    uint8_t  status;     // 状态:0=无效,1=有效
    uint8_t  direction;  // 方向:0=前进,1=后退
};

这个结构体在内存里是分散存放的。但你要把它发到总线上,就必须把它变成一串连续的字节。这个过程,就是序列化。

核心要点:序列化 = 结构化数据 → 字节流

反序列化:反过来“拆包”

反序列化就好理解了。接收方拿到一串字节流,要把它还原成原来的数据结构。这就是“拆包”。

我在项目中遇到过一个问题:有个同事把序列化和反序列化的字节序搞反了。发送方用的是大端序,接收方按小端序解析。结果车速信号读出来是65535,吓得他以为传感器坏了。后来查了半天,才发现是字节序的问题。

所以啊,序列化和反序列化必须严格对称。你用什么规则打包,就必须用什么规则拆包。

我的经验:做SOME/IP序列化时,一定要先确认字节序。AUTOSAR标准默认使用大端序(Big Endian),但有些供应商会自己改成小端序。这个坑我踩过,你们别踩。

序列化在通信中的作用

序列化在汽车通信中到底有多重要?我给你们列几个关键作用:

  • 跨平台通信:不同ECU可能用不同的CPU架构(ARM、PowerPC、TriCore),内存对齐方式也不一样。序列化提供了一种中立的数据表示方式。
  • 网络传输:以太网、CAN、FlexRay这些总线,只认识字节流。你不序列化,数据根本发不出去。
  • 数据持久化:有时候需要把诊断数据或配置参数存到Flash里。序列化后的数据可以直接写入存储介质。
  • 服务发现与调用:SOME/IP协议里,方法的参数、事件的负载,全部依赖序列化来传递。

说白了,没有序列化,ECU之间就是“鸡同鸭讲”。你发过去的是结构体,对方收到的是乱码。

序列化的核心要素

做序列化时,有几个要素必须搞清楚。我整理了一个表格:

要素 说明 常见问题
字节序 大端序还是小端序 发送方和接收方不一致
对齐方式 数据在字节流中的对齐规则 结构体padding导致长度计算错误
数据类型长度 uint8/16/32/64、float、double等 不同平台int长度不同
复杂类型处理 数组、字符串、嵌套结构体 长度字段和实际数据不匹配

嗯,这里要注意:SOME/IP序列化用的是“TLV”思想——Type、Length、Value。但它的实现方式更灵活,后面几章我会详细讲。

一个简单的例子

咱们用C语言模拟一下序列化过程。假设要发送一个车速信号:

// 原始数据
uint32_t speed_value = 6500;  // 表示65.00 km/h
uint8_t  speed_status = 1;    // 有效
uint8_t  speed_dir = 0;       // 前进

// 序列化后的字节流(大端序)
uint8_t buffer[6];
buffer[0] = (speed_value >> 24) & 0xFF;  // 0x00
buffer[1] = (speed_value >> 16) & 0xFF;  // 0x00
buffer[2] = (speed_value >> 8) & 0xFF;   // 0x19
buffer[3] = speed_value & 0xFF;          // 0x64
buffer[4] = speed_status;                // 0x01
buffer[5] = speed_dir;                   // 0x00

你看,一个4字节的uint32_t,拆成了4个单独的字节。接收方拿到这6个字节后,再按照同样的规则拼回去,就能还原出原始数据。

曾经踩过的坑:我曾经在项目里用memcpy直接拷贝结构体到buffer里发送。结果因为结构体有内存对齐的padding,发出去的数据多了几个无用的字节。接收方按固定长度解析,直接解析错位。从那以后,我再也不敢偷懒了,规规矩矩手写序列化函数。

序列化在SOME/IP中的位置

SOME/IP协议栈里,序列化层位于应用层和传输层之间。它的职责很明确:

  • 发送时:把应用层的服务接口(方法调用、事件通知)参数序列化成字节流
  • 接收时:把字节流反序列化成应用层能识别的参数

你可以把它想象成一个翻译官。应用层说“我要发送车速65km/h”,序列化层就把它翻译成“00 00 19 64 01 00”这串字节。传输层只管把这串字节发出去,它不关心内容是什么。

这种分层设计的好处是:应用层开发者不用关心底层网络细节,网络层也不用关心上层业务逻辑。各司其职,互不干扰。

小结

今天咱们讲了三个核心概念:

  1. 序列化:把结构化数据打包成字节流
  2. 反序列化:把字节流拆包还原成结构化数据
  3. 作用:跨平台通信、网络传输、数据持久化、服务调用

下一章,我会带大家深入SOME/IP的序列化规范,看看AUTOSAR标准里到底是怎么定义这些规则的。到时候咱们会手写一个完整的序列化/反序列化函数,你们准备好了吗?

课后思考:如果让你设计一个序列化协议,你会怎么处理可变长度的数组?比如一个长度不固定的字符串。这个问题,咱们下节课解答。