4. SOME/IP报文结构:Message ID、Length、Request ID、Protocol Version、Interface Version、Message Type、Return Code、Payload

好,咱们今天来聊聊SOME/IP报文结构。说实话,这个知识点是诊断通信的基石。你报文结构都搞不清楚,后面排查故障基本就是瞎蒙。我刚开始接触SOME/IP时,也对着抓包数据发过呆——这8个字段到底谁是谁?

别急,我一个一个给你拆开讲。

4.1 Message ID(消息ID)——32位,报文的“身份证”

Message ID,简称MID。它用来唯一标识一个服务或一个事件。说白了,接收方看到这个ID,就知道“哦,这是哪个服务的哪个方法”。

它的结构其实有讲究:

  • 高16位:Service ID(服务ID),标识是哪个服务
  • 低16位:Method ID(方法ID),标识服务里的哪个方法或事件

举个例子:0x1234 0x0001,Service ID是0x1234,Method ID是0x0001。这在AUTOSAR里很常见。

重要区分:

  • Method ID范围0x0001-0x7FFF:方法调用(Method)
  • Method ID范围0x8000-0xFFFF:事件通知(Event)

我遇到过有人把事件ID写成0x0001,结果ECU死活不响应。查了半天,原来是ID范围搞反了。

4.2 Length(长度)——32位,报文有多长?

Length字段表示从Request ID开始到Payload结束的总字节数。注意,它不包含Message ID和Length自身这两个字段。

计算公式很简单:

Length = 8(Request ID + Protocol Version + Interface Version + Message Type + Return Code) + Payload长度

嗯,这里要注意:如果Payload为空,Length就是8。我见过有人把Length算成整个报文长度,结果接收方解析直接崩了。

避坑指南:我曾经在调试时发现,某个ECU发来的Length字段总是比实际大4字节。后来发现是协议栈实现时把Message ID也算进去了。这种问题抓包一看就能发现,但如果你不懂Length的边界,可能排查一整天。

4.3 Request ID(请求ID)——32位,谁在说话?

Request ID用来区分不同的客户端请求。它包含两部分:

  • 高16位:Client ID(客户端ID),标识是哪个客户端
  • 低16位:Session ID(会话ID),标识该客户端的第几次请求

你想想看,如果多个客户端同时调用同一个服务,没有Request ID,服务器根本不知道响应该发给谁。

Session ID每次请求都会递增。我习惯在抓包时盯着Session ID看——如果发现它突然跳变或者归零,基本能判断出客户端那边有复位或者异常。

4.4 Protocol Version(协议版本)——8位,固定值

这个字段目前固定为0x01。代表SOME/IP协议版本1。没什么好说的,你只要知道它存在就行。

不过,我遇到过一台ECU发来的Protocol Version是0x02,结果接收端直接丢弃了报文。后来发现是供应商用了自定义版本。嗯,这种不兼容问题,排查起来很头疼。

4.5 Interface Version(接口版本)——8位,服务接口版本

Interface Version表示服务接口的版本号。比如你的服务从v1.0升级到v2.0,这个字段就会变化。

它的作用是什么?说白了,就是让通信双方确认“咱们用的是同一个版本的服务接口”。如果版本不匹配,服务器可以拒绝请求。

我的经验:在OTA升级场景中,Interface Version特别重要。我曾经遇到升级后ECU不响应诊断请求,一查发现是Interface Version从0x01变成了0x02,而诊断仪还在用0x01的版本请求。解决方案?要么升级诊断仪,要么让ECU兼容旧版本。

4.6 Message Type(消息类型)——8位,报文是干啥的?

这个字段定义了报文的类型。常见的有:

类型 说明
0x00 REQUEST 请求(需要响应)
0x01 REQUEST_NO_RETURN 请求(不需要响应)
0x02 NOTIFICATION 事件通知
0x80 RESPONSE 响应
0x81 ERROR 错误响应

我个人的习惯是:看到0x00就知道这是客户端发来的请求,看到0x80就知道是服务器在回复。如果看到0x81,那基本是出错了。

4.7 Return Code(返回码)——8位,成功还是失败?

Return Code只在响应报文中有效。请求报文里这个字段填0x00。

常见返回码:

含义
0x00 E_OK(成功)
0x01 E_NOT_OK(通用错误)
0x02 E_UNKNOWN_SERVICE(未知服务)
0x03 E_UNKNOWN_METHOD(未知方法)
0x04 E_NOT_READY(服务未就绪)
0x05 E_NOT_REACHABLE(服务不可达)
0x06 E_TIMEOUT(超时)
0x07-0x1F 协议预留

避坑指南:我曾经排查过一个故障,诊断仪发请求后ECU一直返回0x01(E_NOT_OK)。我以为是服务没实现,折腾了两天。后来发现是Payload格式不对——ECU解析失败,只能返回通用错误。所以,看到0x01别急着怀疑服务,先检查Payload对不对。

4.8 Payload(有效载荷)——可变长度,真正的数据

Payload就是实际传输的数据。对于UDS on SOME/IP来说,Payload里装的就是UDS诊断请求或响应。

举个例子,一个UDS诊断请求“读取DTC信息”(0x19 0x02),它的Payload就是:

0x19 0x02

而响应Payload可能是:

0x59 0x02 0x01 0x02 0x03 0x04

嗯,这里要注意:Payload的长度没有上限,但实际受限于底层传输协议(比如UDP的MTU限制)。如果Payload太大,就需要用到SOME/IP的Segmentation(分段传输)机制。

我的经验:在抓包分析时,我习惯先看Message ID确认服务,再看Message Type确认是请求还是响应,最后看Return Code判断是否成功。如果Return Code是0x00,那Payload里的数据基本可信。如果Return Code非0,那Payload可能包含错误信息,需要仔细解析。

4.9 报文结构总览

好了,咱们把8个字段串起来,一个完整的SOME/IP报文长这样:

| Message ID (32bit) | Length (32bit) | Request ID (32bit) | Protocol Version (8bit) | Interface Version (8bit) | Message Type (8bit) | Return Code (8bit) | Payload (可变) |

你想想看,这8个字段加起来,头部固定是16字节(4+4+4+1+1+1+1=16)。加上Payload,就是完整的报文。

我刚开始学的时候,总记不住顺序。后来自己编了个口诀:“消长请求协接返载”——Message ID(消)、Length(长)、Request ID(请求)、Protocol Version(协)、Interface Version(接)、Message Type(返?不对,Message Type是类型,Return Code才是返回码)。嗯,这个口诀不太顺口,你们还是多抓包看吧,看多了自然就记住了。

4.10 实战:抓包分析

假设你抓到一个SOME/IP报文,十六进制如下:

00 01 02 03 00 00 00 10 00 00 00 01 00 00 00 01 01 01 00 00 00 19 02

咱们来拆解:

  • Message ID:00 01 02 03 → Service ID=0x0001, Method ID=0x0203
  • Length:00 00 00 10 → 16字节(从Request ID到Payload结束)
  • Request ID:00 00 00 01 → Client ID=0x0000, Session ID=0x0001
  • Protocol Version:01 → 版本1
  • Interface Version:01 → 接口版本1
  • Message Type:00 → REQUEST(请求)
  • Return Code:00 → 请求报文,返回码无效
  • Payload:00 19 02 → UDS诊断请求(0x19 0x02)

你看,是不是很清晰?

总结一下:

  • Message ID:谁家的什么方法
  • Length:报文有多长(不含前8字节)
  • Request ID:谁发的,第几次发
  • Protocol Version:协议版本,固定0x01
  • Interface Version:服务接口版本
  • Message Type:请求、响应还是通知
  • Return Code:成功还是失败
  • Payload:真正的数据

我个人觉得,掌握这8个字段,SOME/IP报文对你来说就没有秘密了。下一章咱们聊聊UDS on SOME/IP的具体实现,到时候你会看到这些字段是怎么跟诊断服务结合起来的。