4、DoIP报文类型详解:通用DoIP报头、诊断报文、路由激活报文、车辆识别请求报文
好,咱们进入正题。DoIP协议里报文类型不少,但核心的就那么几种。我个人习惯把DoIP报文分成两大类:一类是控制类报文,比如路由激活、车辆识别;另一类是数据类报文,比如诊断报文。今天咱们就把最常用的四种报文掰开揉碎了讲清楚。
4.1 通用DoIP报头(Generic DoIP Header)
所有DoIP报文,不管是什么类型,开头都长一个样。这个公共部分就叫通用DoIP报头。说白了,它就是所有报文的身份证。
它的结构是这样的:
| 字节偏移 | 字段名 | 长度(字节) | 说明 |
|---------|--------|------------|------|
| 0 | 协议版本 | 1 | 当前固定为0x02 |
| 1 | 反向协议版本 | 1 | 取反,用于校验 |
| 2-3 | 负载类型 | 2 | 标识报文类型 |
| 4-7 | 负载长度 | 4 | 后续负载的字节数 |
嗯,这里要注意。协议版本字段目前是0x02,对应ISO 13400-2:2012版本。反向协议版本就是0xFD。为什么要搞个反向版本?我在项目中遇到过,有些设备在电磁干扰环境下会误码,这个反向校验能快速发现报文头损坏。
核心要点:接收方收到报文后,先检查协议版本 + 反向协议版本是否等于0xFF。如果不等于,直接丢弃。这是第一道防线。
负载类型字段很重要。它告诉接收方:后面跟着的是什么报文。比如0x0001是路由激活请求,0x0003是诊断报文,0x0004是车辆识别请求。你想想看,没有这个字段,接收方根本不知道该怎么解析后面的数据。
负载长度字段,顾名思义,就是后面跟着的数据有多少字节。注意,这个长度不包括通用报头本身的8个字节。我曾经见过有人把整个报文长度填进去,结果接收方解析到一半就卡住了。
4.2 诊断报文(Diagnostic Message)
诊断报文是DoIP协议里最常用的报文。说白了,它就是UDS诊断请求和响应的搬运工。DoIP本身不关心诊断数据的内容,它只负责把诊断数据从客户端传到服务端,再把响应传回来。
诊断报文的格式:
| 字节偏移 | 字段名 | 长度(字节) | 说明 |
|---------|--------|------------|------|
| 0-1 | 源地址 | 2 | 发送方的逻辑地址 |
| 2-3 | 目标地址 | 2 | 接收方的逻辑地址 |
| 4... | 诊断数据 | 可变 | UDS请求/响应 |
这里有个坑。源地址和目标地址是逻辑地址,不是IP地址。逻辑地址是DoIP实体在车辆网络中的唯一标识。比如网关的逻辑地址是0x0E80,某个ECU的逻辑地址是0x1001。这些地址在项目初期就要规划好,不然后面改起来很麻烦。
避坑指南:我曾经在一个项目里,两个ECU的逻辑地址配反了。结果诊断仪发请求给ECU A,ECU B却收到了。排查了整整两天才发现是地址配置问题。所以,逻辑地址表一定要在项目启动时就定下来,并且做交叉评审。
诊断数据部分就是标准的UDS报文。比如0x10 0x03是诊断会话控制请求,0x22 0xF1 0x90是读取VIN码的请求。DoIP协议栈不需要理解这些数据,只需要原封不动地转发。
嗯,这里要提一下。诊断报文可以分段发送。如果诊断数据超过某个阈值(通常是4KB),就需要用DoIP的分段传输机制。这个咱们后面章节会详细讲。
4.3 路由激活报文(Routing Activation)
路由激活报文,说白了就是客户端向服务端申请建立诊断通信通道。没有这个步骤,服务端不会理你发来的任何诊断报文。
路由激活请求的格式:
| 字节偏移 | 字段名 | 长度(字节) | 说明 |
|---------|--------|------------|------|
| 0-1 | 源地址 | 2 | 客户端的逻辑地址 |
| 2 | 激活类型 | 1 | 0x00=默认, 0x01=WWH-OBD |
| 3-6 | 保留 | 4 | 必须为0 |
| 7-10 | OBD扩展 | 4 | 可选,用于WWH-OBD |
激活类型字段,大部分场景下用0x00就够了。0x01是WWH-OBD模式,用于排放相关的诊断。我在实际项目中几乎没用过WWH-OBD,除非客户有明确要求。
服务端收到路由激活请求后,会返回一个路由激活响应:
| 字节偏移 | 字段名 | 长度(字节) | 说明 |
|---------|--------|------------|------|
| 0-1 | 源地址 | 2 | 客户端的逻辑地址 |
| 2 | 激活类型 | 1 | 同请求中的值 |
| 3 | 响应码 | 1 | 0x00=成功, 其他=失败 |
| 4-7 | 保留 | 4 | 必须为0 |
重要提醒:响应码为0x00表示路由激活成功。如果返回0x01~0x06,分别表示不同的错误原因。比如0x01表示未知源地址,0x02表示不支持该激活类型。我曾经遇到一个情况,客户端发路由激活请求时源地址填了0x0000,服务端直接返回0x01拒绝。因为0x0000是保留地址,不能用作客户端逻辑地址。
路由激活成功后,服务端会把这个客户端的IP地址和逻辑地址绑定在一起。后续该客户端发来的诊断报文,只要源地址匹配,服务端就会处理。否则直接丢弃。
4.4 车辆识别请求报文(Vehicle Identification Request)
车辆识别请求,顾名思义,就是用来发现车辆信息的。诊断仪刚连接到车辆网络时,不知道有哪些DoIP实体在线,也不知道它们的VIN码、EID等信息。这时候就需要发一个车辆识别请求。
车辆识别请求有两种模式:
- 广播模式:发送到255.255.255.255,所有DoIP实体都会响应
- 单播模式:发送到特定IP地址,只有该实体响应
请求报文格式很简单:
| 字节偏移 | 字段名 | 长度(字节) | 说明 |
|---------|--------|------------|------|
| 0-2 | VIN | 3 | 可选,用于VIN过滤 |
| 3-4 | EID | 2 | 可选,用于EID过滤 |
| 5-6 | GID | 2 | 可选,用于GID过滤 |
| 7 | 保留 | 1 | 必须为0 |
如果所有过滤字段都填0,表示请求所有DoIP实体响应。如果填了VIN,则只有VIN匹配的实体才响应。
响应报文格式:
| 字节偏移 | 字段名 | 长度(字节) | 说明 |
|---------|--------|------------|------|
| 0-16 | VIN | 17 | 车辆识别码 |
| 17-18 | 逻辑地址 | 2 | DoIP实体的逻辑地址 |
| 19-24 | EID | 6 | 实体标识符 |
| 25-30 | GID | 6 | 组标识符 |
| 31 | 保留 | 1 | 必须为0 |
| 32-33 | 进一步动作 | 2 | 0x0000=无进一步动作 |
实战经验:我在做车载网关项目时,发现车辆识别响应报文里VIN字段必须填17个字节,不足的部分用0x00填充。有些供应商只填了实际VIN的长度,结果诊断仪解析时把后面的EID字段也当成VIN的一部分,导致EID解析错误。这种问题很难排查,因为VIN和EID看起来都是乱码。
车辆识别请求还有一个变种,叫车辆识别请求(带EID)。它只匹配EID字段,不匹配VIN。这在某些场景下很有用,比如你知道某个ECU的EID,但不知道它的IP地址。
好了,这四种报文是DoIP协议栈的基础。通用报头是所有报文的骨架,诊断报文是核心业务,路由激活是准入机制,车辆识别是发现机制。把这四种报文吃透了,DoIP协议栈的框架就搭起来了。
下一章咱们会讲DoIP的连接管理和状态机,那才是真正考验协议栈健壮性的地方。