3、DoIP报文格式:通用报文头结构、负载类型与代码定义、诊断报文格式

好,咱们今天聊聊DoIP的报文格式。说实话,这是整个DoIP协议里最“死板”但也最核心的部分。你想想看,车载网络里那么多ECU,大家要能互相听懂,报文格式就必须统一。我当年刚接触DoIP时,就被这个报文头折腾过几次,后来发现只要理清了结构,其实很简单。

3.1 通用报文头结构

每个DoIP报文,开头都是一个固定的头部。这个头部一共8个字节,不多不少。我个人习惯把这8个字节分成四个字段来记:

字节偏移 字段名称 长度 说明
0-1 协议版本号 2字节 当前标准值为0x0002(ISO 13400-2)
2-3 负载类型 2字节 标识报文的具体用途
4-7 负载长度 4字节 后续数据的字节数(不含头部)

嗯,这里要注意:协议版本号目前主流是0x0002,但有些老项目还在用0x0001。我曾经在兼容性测试时踩过这个坑——新版的Tester发了个0x0002的报文,老ECU直接不认。所以做网关开发时,最好两边都支持。

核心要点:通用报文头是DoIP通信的“身份证”。任何DoIP报文,前8个字节必须严格按照这个结构来。少一个字节,接收方就会直接丢弃。

3.2 负载类型与代码定义

负载类型字段,说白了就是告诉对方“我这条报文是干嘛用的”。DoIP协议里定义了好几种类型,我挑几个最常用的说说:

  • 0x0001(Generic DoIP header negative acknowledge):通用否定应答。当接收方发现报文头有问题时,就用这个回复。比如版本号不匹配、负载长度不对等。
  • 0x0002(Vehicle Identification Request):车辆识别请求。Tester上线时发的第一条报文,用来发现网络里有哪些ECU。
  • 0x0004(Vehicle Identification Response):车辆识别应答。ECU收到0x0002后,回复自己的VIN码等信息。
  • 0x8001(Diagnostic Message):诊断报文。这是咱们最常用的类型,承载UDS诊断请求和响应。

我记得有一次项目联调,Tester发诊断请求总是没响应。抓包一看,负载类型写成了0x0001。你想想看,ECU收到后以为是个否定应答,直接忽略了。这种低级错误,排查起来最费时间。

避坑指南:我曾经在写代码时,把负载类型和负载长度搞反了顺序。结果报文发出去,ECU解析出来的长度是个天文数字,直接内存溢出。所以写代码时,一定要对照协议文档逐字节核对。

3.3 诊断报文格式

诊断报文(负载类型0x8001)是DoIP里最复杂的部分。它除了通用报文头外,还有自己的子结构。我习惯把它分成三层来看:

  1. 通用报文头:8字节,前面讲过了。
  2. 诊断报文头:4字节,包含源地址、目标地址等。
  3. UDS数据:真正的诊断请求/响应内容。

诊断报文头的4个字节结构如下:

字节偏移 字段名称 长度 说明
0-1 源地址 2字节 发送方的逻辑地址
2-3 目标地址 2字节 接收方的逻辑地址

举个例子,Tester想读取ECU的软件版本号,完整的诊断报文应该是这样的:

// 通用报文头(8字节)
0x00 0x02  // 协议版本号
0x80 0x01  // 负载类型:诊断报文
0x00 0x00 0x00 0x04  // 负载长度:4字节(诊断报文头+UDS数据)

// 诊断报文头(4字节)
0x0E 0x80  // 源地址:Tester的逻辑地址
0x00 0x01  // 目标地址:ECU的逻辑地址

// UDS数据(2字节)
0x22 0xF1 0x90  // 读取数据标识符0xF190(软件版本号)

你可能会问:为什么源地址和目标地址要占2个字节?因为DoIP支持逻辑地址范围是0x0000到0xFFFF,足够覆盖整车所有ECU了。我见过有些设计把地址写死成0x0E80,结果换了Tester就通信失败。所以地址最好做成可配置的。

重要提醒:诊断报文头的源地址和目标地址,必须和路由激活阶段协商的地址一致。否则ECU会认为这是非法报文,直接丢弃。我曾经在测试时忘了更新路由表,折腾了半天才发现是地址没对上。

好了,关于DoIP报文格式,今天就聊这么多。下一章咱们会深入讲讲路由激活的过程,那才是DoIP通信真正开始的地方。你先把报文结构记牢,后面用起来就顺手了。