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;
}

运行与验证

  1. 将上述代码保存为 someip_codec.cpp。
  2. 编译:g++ -std=c++11 -o someip_codec someip_codec.cpp
  3. 运行:./someip_codec
  4. 预期输出: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位字段,便于业务层直接使用服务与方法标识。