2. SOME/IP报文序列化与反序列化
任务目标
实现SOME/IP报文的核心序列化与反序列化逻辑,覆盖请求/响应报文中最关键的字段:Message ID、Request ID、协议版本、消息类型、返回码以及Payload。通过本案例,你将获得一个不依赖第三方库、可直接嵌入车载通信原型的最小C++实现,并能够验证报文在字节流与结构体之间的正确转换。
环境与依赖
- 编译器:支持C++11或更高版本(如g++ 7.5+)
- 无第三方库依赖,仅使用标准库
关键完整代码
#include <cstdint>
#include <cstring>
#include <vector>
#include <stdexcept>
#include <iostream>
// SOME/IP 报文头长度(不含Payload)
static constexpr uint32_t SOMEIP_HEADER_LEN = 16;
// 消息类型常量
enum SomeIpMsgType : uint8_t {
REQUEST = 0x00,
RESPONSE = 0x80
};
// 返回码常量
enum SomeIpReturnCode : uint8_t {
E_OK = 0x00,
E_NOT_OK = 0x01
};
// SOME/IP 报文结构(网络字节序大端)
struct SomeIpMessage {
uint16_t service_id; // Message ID 高16位
uint16_t method_id; // Message ID 低16位
uint32_t length; // 从Request ID开始到Payload末尾的总长度
uint32_t request_id; // 客户端生成的请求标识
uint8_t protocol_version; // 固定为0x01
uint8_t interface_version;// 接口版本
uint8_t msg_type; // 消息类型
uint8_t return_code; // 返回码
std::vector<uint8_t> payload;
};
// 序列化:结构体 -> 字节流
std::vector<uint8_t> serialize(const SomeIpMessage& msg) {
std::vector<uint8_t> buf;
buf.reserve(SOMEIP_HEADER_LEN + msg.payload.size());
// Message ID (32位)
buf.push_back(static_cast<uint8_t>(msg.service_id >> 8));
buf.push_back(static_cast<uint8_t>(msg.service_id & 0xFF));
buf.push_back(static_cast<uint8_t>(msg.method_id >> 8));
buf.push_back(static_cast<uint8_t>(msg.method_id & 0xFF));
// Length (32位) = Request ID(4) + Protocol Ver(1) + Interface Ver(1)
// + Msg Type(1) + Return Code(1) + Payload长度
uint32_t len = 8 + static_cast<uint32_t>(msg.payload.size());
buf.push_back(static_cast<uint8_t>(len >> 24));
buf.push_back(static_cast<uint8_t>((len >> 16) & 0xFF));
buf.push_back(static_cast<uint8_t>((len >> 8) & 0xFF));
buf.push_back(static_cast<uint8_t>(len & 0xFF));
// Request ID (32位)
buf.push_back(static_cast<uint8_t>(msg.request_id >> 24));
buf.push_back(static_cast<uint8_t>((msg.request_id >> 16) & 0xFF));
buf.push_back(static_cast<uint8_t>((msg.request_id >> 8) & 0xFF));
buf.push_back(static_cast<uint8_t>(msg.request_id & 0xFF));
// 协议版本、接口版本、消息类型、返回码
buf.push_back(msg.protocol_version);
buf.push_back(msg.interface_version);
buf.push_back(msg.msg_type);
buf.push_back(msg.return_code);
// Payload
buf.insert(buf.end(), msg.payload.begin(), msg.payload.end());
return buf;
}
// 反序列化:字节流 -> 结构体
SomeIpMessage deserialize(const std::vector<uint8_t>& buf) {
if (buf.size() < SOMEIP_HEADER_LEN) {
throw std::runtime_error("Buffer too short for SOME/IP header");
}
SomeIpMessage msg;
size_t off = 0;
// Message ID
msg.service_id = (static_cast<uint16_t>(buf[off]) << 8) | buf[off+1];
msg.method_id = (static_cast<uint16_t>(buf[off+2]) << 8) | buf[off+3];
off += 4;
// Length
uint32_t len = (static_cast<uint32_t>(buf[off]) << 24) |
(static_cast<uint32_t>(buf[off+1]) << 16) |
(static_cast<uint32_t>(buf[off+2]) << 8) |
static_cast<uint32_t>(buf[off+3]);
msg.length = len;
off += 4;
// 校验长度字段与实际剩余字节数是否一致
if (buf.size() < off + len) {
throw std::runtime_error("Length field exceeds buffer size");
}
// Request ID
msg.request_id = (static_cast<uint32_t>(buf[off]) << 24) |
(static_cast<uint32_t>(buf[off+1]) << 16) |
(static_cast<uint32_t>(buf[off+2]) << 8) |
static_cast<uint32_t>(buf[off+3]);
off += 4;
// 协议版本、接口版本、消息类型、返回码
msg.protocol_version = buf[off++];
msg.interface_version = buf[off++];
msg.msg_type = buf[off++];
msg.return_code = buf[off++];
// Payload(剩余字节)
msg.payload.assign(buf.begin() + off, buf.begin() + off + (len - 8));
return msg;
}
// 最小验证入口
int main() {
// 构造一个请求报文
SomeIpMessage req;
req.service_id = 0x1234;
req.method_id = 0x0001;
req.request_id = 0xABCDEF01;
req.protocol_version = 0x01;
req.interface_version = 0x01;
req.msg_type = REQUEST;
req.return_code = E_OK;
req.payload = {0xDE, 0xAD, 0xBE, 0xEF};
// 序列化
auto bytes = serialize(req);
// 反序列化
SomeIpMessage parsed = deserialize(bytes);
// 验证关键字段是否一致
bool ok = (parsed.service_id == req.service_id) &&
(parsed.method_id == req.method_id) &&
(parsed.request_id == req.request_id) &&
(parsed.protocol_version == req.protocol_version) &&
(parsed.interface_version == req.interface_version) &&
(parsed.msg_type == req.msg_type) &&
(parsed.return_code == req.return_code) &&
(parsed.payload == req.payload);
if (ok) {
std::cout << "PASS: Serialization round-trip verified" << std::endl;
} else {
std::cout << "FAIL: Field mismatch detected" << std::endl;
return 1;
}
return 0;
}
运行与验证
- 将上述代码保存为
someip_codec.cpp。 - 编译:
g++ -std=c++11 -o someip_codec someip_codec.cpp - 运行:
./someip_codec - 预期输出:
PASS: Serialization round-trip verified,程序退出码为0。
该验证通过构造一个包含非零Payload的请求报文,执行序列化后再反序列化,并逐字段比对原始值与解析值。若所有字段完全一致,则证明编解码逻辑正确。Length字段在反序列化时用于边界校验,确保不会越界读取。
关键说明
- 本实现采用大端(网络字节序)编码,与SOME/IP规范一致。
- Length字段的值等于Request ID(4字节) + 协议版本(1) + 接口版本(1) + 消息类型(1) + 返回码(1) + Payload长度,即8 + payload.size()。
- 反序列化时对Length字段做了边界检查,防止畸形报文导致越界访问。
- Message ID被拆分为service_id和method_id两个16位字段,便于业务层直接使用服务与方法标识。