3、DoIP报文结构:通用报文头、诊断报文、路由激活报文、车辆信息请求报文详解

好,咱们今天来啃一块硬骨头——DoIP的报文结构。说实话,我刚接触DoIP那会儿,看着那一堆字节流,头都大了。但后来我发现,只要把几个核心报文搞明白,剩下的都是套路。今天我就带你把这四种报文掰开揉碎了讲清楚。

3.1 通用报文头:所有报文的“身份证”

每个DoIP报文,不管干啥用的,前面都得挂一个通用报文头。你可以把它想象成快递单上的收件人信息——没有它,数据就不知道往哪儿送。

这个报文头固定是8个字节,结构如下:

字段 长度(字节) 说明
协议版本号 1 当前固定为0x02(ISO 13400-2:2012)
反向协议版本号 1 取反,用于校验,0xFD
负载类型 2 标识后面跟着的是什么报文
负载长度 4 后面数据的字节数,不包括这8字节头

这里有个坑,我必须要提醒你。负载类型字段是2个字节,但很多初学者只看了低字节,高字节直接忽略。我在项目中就遇到过,某个供应商的ECU把高字节填成了0x01,结果我们的工具死活解析不出来。排查了一整天,最后发现是协议版本号对不上。嗯,这种低级错误,真的会让人崩溃。

避坑指南: 我曾经在测试中发现,有些ECU在发送车辆信息请求响应时,负载长度字段填的是整个报文长度(包括报文头)。这明显不符合规范。记住:负载长度只算报文头后面的数据,千万别被带偏了。

3.2 诊断报文:核心中的核心

诊断报文,说白了就是UDS over DoIP。它的负载类型是0x8001(请求)和0x8002(响应)。结构其实很简单:

| 通用报文头 (8字节) | 源地址 (2字节) | 目标地址 (2字节) | UDS数据 (N字节) |

你想想看,为什么要有源地址和目标地址?因为DoIP是网络层协议,它得知道这条诊断请求是从哪个ECU发出来的,要发给谁。这个地址就是ISO 13400里定义的逻辑地址。

我个人习惯,在解析诊断报文时,会先检查源地址和目标地址是否在预定义的范围内。比如,诊断仪的逻辑地址通常是0x0E80,而ECU的地址范围是0x0001到0x0E7F。如果源地址是0x0000,那基本可以断定这条报文有问题。

关键点: UDS数据部分就是标准的诊断请求/响应,比如10 03(诊断会话控制)、22 F1 90(读取数据标识符)。DoIP只负责传输,不关心UDS具体内容。

3.3 路由激活报文:建立通信的“握手”

路由激活报文,负载类型是0x0005(请求)和0x0006(响应)。它的作用是让诊断仪和ECU之间建立一条逻辑路由。没有这一步,你发出去的诊断报文ECU根本不会理你。

请求报文结构:

| 通用报文头 (8字节) | 源地址 (2字节) | 激活类型 (1字节) | 保留 (5字节) | OEM特定数据 (可选) |

激活类型字段很关键。0x00表示默认激活,0x01表示带安全认证的激活。我记得有一次,客户反馈说他们的诊断仪连不上ECU,我远程一看,发现激活类型填的是0x02,而ECU只支持0x00和0x01。这就是典型的“鸡同鸭讲”。

响应报文结构:

| 通用报文头 (8字节) | 源地址 (2字节) | 目标地址 (2字节) | 激活状态 (1字节) | 保留 (4字节) | OEM特定数据 (可选) |

激活状态0x10表示成功,0x11~0x13表示各种失败原因。我建议你在调试时,重点关注这个字段。如果返回0x11(拒绝),那大概率是源地址已经被占用了。

小技巧: 我曾经在项目中遇到一个奇怪的问题——路由激活总是失败,但抓包看报文格式完全正确。后来发现是ECU的TCP socket接收缓冲区太小,导致报文被截断了。所以,如果你确认报文格式没问题,不妨检查一下底层网络参数。

3.4 车辆信息请求报文:快速获取车辆“身份”

这个报文很有意思,负载类型是0x0001(请求)和0x0002(响应)。它的作用是让诊断仪快速获取车辆的基本信息,比如VIN码、ECU的IP地址、逻辑地址等。注意,这个报文不需要路由激活,直接就能发。

请求报文非常简单:

| 通用报文头 (8字节) | 标识符类型 (1字节) | 标识符值 (可变) |

标识符类型字段:

  • 0x01:请求VIN码
  • 0x02:请求所有ECU的IP地址和逻辑地址
  • 0x03:请求特定ECU的信息

响应报文结构:

| 通用报文头 (8字节) | 标识符类型 (1字节) | 数据 (可变) |

举个例子,如果你请求VIN码(标识符类型=0x01),响应数据就是17字节的VIN字符串。如果你请求所有ECU信息(标识符类型=0x02),响应数据会包含一个列表,每个ECU的信息包括逻辑地址(2字节)、IP地址(4字节)、端口号(2字节)等。

这里有个细节,我刚开始做DoIP时没注意。车辆信息请求的响应数据中,ECU列表的长度是不固定的。你不能假设只有1个ECU。我曾经在测试中遇到一辆车有8个ECU,结果我的工具只解析了前3个,后面的全丢了。嗯,从那以后,我写代码时都会用循环来解析,而不是固定长度。

总结一下: 车辆信息请求报文是DoIP的“敲门砖”。你连上车辆后,第一件事就是发这个报文,获取所有ECU的信息。然后才能进行路由激活和后续的诊断操作。

3.5 报文交互流程:把这些报文串起来

光讲单个报文没意思,咱们看看它们在实际中是怎么配合的。一个典型的DoIP诊断流程是这样的:

  1. 诊断仪发送车辆信息请求(0x0001),获取所有ECU的IP和逻辑地址。
  2. 诊断仪选择目标ECU,发送路由激活请求(0x0005)。
  3. ECU返回路由激活响应(0x0006),状态为0x10(成功)。
  4. 诊断仪发送诊断请求(0x8001),比如10 03。
  5. ECU返回诊断响应(0x8002),比如50 03。

你看,是不是很清晰?每一步都有对应的报文类型。我个人习惯,在开发诊断工具时,会先实现车辆信息请求和路由激活这两个基础功能。只要这两个通了,后面的诊断报文基本不会出问题。

注意: 路由激活成功后,诊断仪和ECU之间会建立一个逻辑连接。这个连接是有超时时间的。如果超过5秒没有诊断报文交互,连接会自动断开。我曾经在测试中遇到过,因为网络延迟导致诊断响应超时,结果连接断了,后续的报文全部失败。所以,建议你在发送诊断请求时,设置一个合理的超时重传机制。

好了,DoIP的四种核心报文就讲到这里。你可能会问,为什么没有讲Alive Check报文和诊断电源模式请求报文?别急,那些是后面的章节。今天先把这四种吃透,后面的就水到渠成了。