第二章:DoIP协议栈架构:TCP/IP协议栈与DoIP的关系、UDP与TCP的职责划分、DoIP实体与逻辑地址
好,咱们进入正题。这一章我打算聊聊DoIP协议栈的骨架——它到底是怎么搭在TCP/IP上面的,UDP和TCP各自管什么,还有那个让人容易绕晕的DoIP实体和逻辑地址。
说实话,我刚接触DoIP那会儿,也犯过糊涂。总觉得不就是把诊断数据塞进IP包嘛,有啥复杂的?后来踩了几个坑才明白,这里面的门道还真不少。你想想看,车载诊断要的是可靠,但又要快,这两者怎么平衡?嗯,这就是UDP和TCP分工的由来。
2.1 TCP/IP协议栈与DoIP的关系
DoIP,全称是Diagnostic over Internet Protocol。说白了,就是把传统的CAN诊断报文,搬到以太网上来跑。但问题来了——以太网是“尽力而为”的,诊断可不行,丢一个报文可能就导致ECU刷写失败。
所以DoIP并没有 reinvent the wheel,它直接站在了TCP/IP的肩膀上。我习惯把DoIP协议栈分成四层来看:
- 物理层:100BASE-TX 或 1000BASE-T,现在新车很多用BroadR-Reach(100BASE-T1),一对双绞线搞定。
- 网络层与传输层:IPv4/IPv6 + UDP/TCP。这里就是DoIP的“交通管制员”。
- DoIP协议层:咱们测试的重点。它定义了报文头、负载类型、错误码等。
- 应用层:UDS诊断服务(ISO 14229-1),比如读取故障码、刷写固件。
我记得有一次在台架上测试,发现DoIP报文老是超时。查了半天,结果是TCP的Nagle算法在作祟——它把小包合并了再发,诊断响应就慢了。从那以后,我每次做DoIP测试都会先检查一下TCP_NODELAY有没有置上。
核心要点:DoIP不是一个新的传输协议,它只是给UDS诊断报文穿上了IP的外衣。TCP/IP负责“怎么传”,DoIP负责“传什么”和“传给谁”。
2.2 UDP与TCP的职责划分
很多初学者会问:为什么DoIP要用两个传输层协议?一个TCP不够吗?
答案是:不够。因为诊断场景分两种——一种是“发现设备”,另一种是“传输数据”。这两种场景对网络的要求完全不同。
咱们来看一张表,这是我做项目时自己总结的:
| 特性 | UDP(车辆发现/路由激活) | TCP(诊断消息/数据上传下载) |
|---|---|---|
| 连接方式 | 无连接 | 面向连接 |
| 可靠性 | 不可靠(无重传) | 可靠(有确认和重传) |
| 典型端口 | UDP 13400 | TCP 13400 |
| 主要用途 | 车辆声明、路由激活请求 | 诊断请求/响应、刷写数据 |
| 报文大小限制 | 受限于MTU(通常1500字节) | 无限制(可分段) |
UDP的活儿:说白了就是“喊一嗓子”。测试设备发一个广播,问“谁在?”,车上的DoIP实体听到后,用UDP回一句“我在,这是我的逻辑地址”。这个过程不需要建立连接,快就一个字。但你也别指望它保证送达——丢了就丢了,所以车辆声明报文会重复发几次。
TCP的活儿:一旦找到了设备,正经的诊断通信就得走TCP了。为什么?因为诊断报文不能丢。你想想,刷写ECU的时候,如果少了一个数据块,整个ECU可能就变砖了。TCP的三次握手、确认重传、流量控制,这些机制保证了诊断数据的完整到达。
我曾经在测试一个OEM的DoIP实现时,发现车辆声明报文总是丢包。排查下来,是UDP的校验和计算有bug,某些平台下校验和字段填了0。嗯,这里要注意——UDP校验和为0在某些网络设备上会被直接丢弃。
实战技巧:在做一致性测试时,我建议你重点关注UDP的“车辆声明”和TCP的“连接建立”这两个阶段。很多ECU的DoIP实现问题,都出在这两个入口上。
2.3 DoIP实体与逻辑地址
好,接下来咱们聊聊DoIP实体和逻辑地址。这个概念,说实话,我第一次看ISO 13400标准时也绕了半天。
DoIP实体是什么?你可以把它理解成“一个能说话的东西”。在车上,一个DoIP实体可以是一个ECU(比如网关),也可以是一个测试设备(比如诊断仪)。每个DoIP实体都有一个唯一的逻辑地址。
逻辑地址分两种:
- 物理地址:指向一个具体的DoIP实体。比如网关的逻辑地址是0x0001,诊断仪的逻辑地址是0x0E00。
- 功能地址:指向一组DoIP实体。比如所有车门ECU的功能地址是0x0100,发到这个地址的报文,所有车门ECU都会处理。
我习惯用“门牌号”来类比:物理地址就是你家门牌,快递员直接送到你手上;功能地址就是小区广播,喊一嗓子,所有住户都能听到。
在DoIP协议里,逻辑地址是16位的,范围从0x0000到0xFFFF。但实际使用时,ISO 13400做了分区:
| 地址范围 | 用途 |
|---|---|
| 0x0000 | 保留 |
| 0x0001 - 0x0DFF | 车载DoIP实体(ECU) |
| 0x0E00 - 0x0EFF | 外部测试设备 |
| 0x0F00 - 0xFFFF | 保留或OEM自定义 |
这里有个坑,我踩过。有一次测试,诊断仪发了一个路由激活请求,目标逻辑地址填的是0x0001。但网关返回了“未知目标地址”的错误。查了半天,发现网关的DoIP实体逻辑地址配置成了0x0002。你想想看,地址都配错了,怎么可能通信成功?
避坑指南:我曾经遇到过一台ECU,它的DoIP实体逻辑地址和UDS诊断会话里的物理地址不一致。结果就是:DoIP层能连上,但UDS诊断请求发过去后,ECU根本不认。所以做测试时,一定要确认这两个地址是匹配的。
再说说DoIP实体的状态机。每个DoIP实体在启动后,会经历几个阶段:
- 未配置:刚上电,还没拿到IP地址。
- 已配置:拿到了IP,但还没注册逻辑地址。
- 已注册:逻辑地址分配完成,可以接收路由激活请求。
- 已激活:路由激活成功,进入诊断模式。
我个人习惯在测试脚本里,先检查DoIP实体的状态,再发诊断请求。如果状态不对,后面的测试全是白费功夫。
最后,聊一下逻辑地址的分配方式。ISO 13400支持两种:
- 静态分配:出厂时就写死了,比如网关永远是0x0001。这种方式简单,但不够灵活。
- 动态分配:上电后通过DHCP或AutoIP拿到IP,再通过DoIP的“实体状态请求”来协商逻辑地址。这种方式在复杂的域控制器架构里很常见。
我记得有一次做动态分配的测试,ECU在DHCP续租时IP变了,但逻辑地址没跟着更新。结果诊断仪还拿着旧的IP去连,自然连不上。嗯,这个场景在一致性测试里一定要覆盖到。
总结一下:DoIP实体就是通信的“人”,逻辑地址就是“身份证号”。UDP负责“找人”,TCP负责“聊天”。搞清楚了这三者的关系,DoIP协议栈的架构你就掌握了七成。
下一章,咱们会深入DoIP的报文头结构,看看那8个字节里到底藏了什么秘密。到时候我会拿一个真实的抓包数据来拆解,保证让你一看就懂。