4、DoIP消息格式:通用消息头结构、Payload类型定义、否定响应码

好,咱们今天聊聊DoIP的消息格式。说实话,这是整个协议里最“死板”但也最核心的部分。你想想看,车上的ECU和诊断仪要对话,总得有个统一的“信封”吧?这个信封怎么封口、里面装什么、对方看不懂怎么回——就是咱们这节课要讲的内容。

4.1 通用消息头结构:这个“信封”长什么样?

DoIP的通用消息头,说白了就是前8个字节。我习惯把这8个字节分成四部分来记:协议版本、版本取反、负载类型、负载长度。嗯,咱们一个一个看。

字节偏移 字段名称 长度(字节) 说明
0 协议版本 1 当前固定为0x02(ISO 13400-2:2012)
1 版本取反 1 协议版本的按位取反,用于校验
2-3 负载类型 2 标识后续Payload的具体含义
4-7 负载长度 4 Payload的字节数(不含消息头)

这里有个小细节我特别想强调——版本取反这个字段。我在项目中遇到过好几次,有人把版本号写对了,但取反算错了,结果诊断仪直接丢弃报文。你想想看,接收方收到报文后,会把版本字段和取反字段做异或,结果必须等于0xFF。否则,嗯,直接扔掉,不解释。

核心要点:消息头校验失败时,接收方不会回复任何否定响应码,直接静默丢弃。这是DoIP的设计哲学——连错误响应都不给,避免无效通信。

再说说负载长度。这个字段是4个字节,最大能表示4GB的Payload。但实际项目中,我建议你控制在64KB以内。为什么?因为很多MCU的接收缓冲区就这么大,你发个几MB的数据过去,ECU直接内存溢出复位了。我曾经吃过这个亏,后来老老实实加了分片传输。

4.2 Payload类型定义:信封里装什么?

负载类型字段占2个字节,目前定义了大概20多种。我把最常用的几个列出来,你记牢了,面试和干活都用得上。

负载类型值(十六进制) 名称 方向 用途
0x0001 通用DoIP头否定确认 ECU → 测试仪 消息头校验失败时回复
0x0002 车辆识别请求 测试仪 → ECU 获取车辆基本信息
0x0003 车辆识别响应 ECU → 测试仪 返回VIN、EID等信息
0x0004 路由激活请求 测试仪 → ECU 建立诊断逻辑连接
0x0005 路由激活响应 ECU → 测试仪 确认激活结果
0x8001 诊断消息 双向 承载UDS诊断请求/响应

我个人习惯把负载类型分成三类:控制类(0x0001-0x00FF)、数据类(0x0100-0x7FFF)、诊断类(0x8000-0xFFFF)。你看0x8001就是诊断消息,这是咱们最常用的。说白了,你平时用诊断仪读故障码、刷写固件,走的都是这个类型。

小技巧:在AUTOSAR的DoIP模块配置中,负载类型对应的处理函数是通过函数指针表实现的。我建议你把这张表做成可配置的,方便后期扩展自定义负载类型。别问我怎么知道的——我被客户要求加过私有协议,当时没预留接口,改得想哭。

4.3 否定响应码:对方说“不”的时候

DoIP的否定响应码,和UDS的NRC不太一样。UDS的NRC是0x10-0x7F,而DoIP的否定响应码是单独的枚举值,放在通用DoIP头否定确认(0x0001)的Payload里。

我整理了一份最全的否定响应码表,你收藏好:

代码(十进制) 代码(十六进制) 含义 触发场景
0x00 0x00 否定确认 通用否定,具体原因见子错误码
0x01 0x01 不支持的协议版本 版本字段不是0x02,或取反校验失败
0x02 0x02 不支持的消息类型 负载类型值未定义或不被支持
0x03 0x03 连接被拒绝 TCP连接数已达上限
0x04 0x04 未知的负载长度 负载长度与实际接收数据不匹配
0x05 0x05 无效的负载数据 Payload内容格式错误
0x06 0x06 路由激活被拒绝 客户端认证失败或激活条件不满足
0x07 0x07 未知的源地址 逻辑地址未在路由表中注册

这里有个坑,我必须要提醒你——0x00否定确认。这个代码本身不告诉你具体错在哪,它后面会跟一个1字节的子错误码。但很多初学者只看主错误码,不看子错误码,排查问题的时候绕了一大圈。我曾经花了一整天,最后发现是子错误码0x13(TLS握手失败),而主错误码只是0x00。

避坑指南:当收到0x00否定确认时,一定要解析后续的子错误码。子错误码的定义在ISO 13400-2的附录中有详细说明。另外,有些ECU实现不规范,0x00后面可能没有子错误码——遇到这种情况,我建议你直接升级ECU固件,别惯着。

再说一个实际案例。有一次我在做DoIP网关集成测试,发现车辆识别请求总是返回0x02(不支持的消息类型)。我检查了负载类型字段,0x0002没错啊。后来抓包一看,原来是消息头里的协议版本写成了0x01(老版本)。接收方先校验版本,发现不对,直接回了0x02。嗯,这里要注意——否定响应码的优先级:版本校验 > 类型校验 > 长度校验 > 内容校验。版本不对,后面的都不看了。

最后,我建议你在实现DoIP协议栈时,把否定响应码的生成逻辑做成一个独立模块。每个错误码对应一个处理函数,方便调试。我在AUTOSAR项目中就是这么做的,后来客户要求增加私有错误码,我只用加一个枚举值和对应的处理函数,半小时搞定。

好了,消息格式这块就讲这么多。下一节咱们聊聊DoIP的连接建立流程,那才是真正开始“握手”的地方。