2. DoIP物理层与传输层:以太网物理层基础、TCP/UDP在DoIP中的应用、DoIP报文格式解析
好,咱们进入正题。这一章聊的是DoIP的物理层和传输层。说白了,就是数据到底怎么从一根网线里跑起来,又怎么被TCP/UDP打包送出去。我刚开始接触车载以太网时,总觉得这跟IT以太网没啥区别,后来踩了几个坑才明白——车上的环境,真不一样。
2.1 以太网物理层基础:车规级到底改了啥?
物理层,就是最底层的那套东西。信号怎么编码、怎么传输、用啥线缆。DoIP用的是标准以太网物理层,但针对汽车做了不少改动。
我个人习惯把车载以太网物理层分成三类:
- 100BASE-T1:单对非屏蔽双绞线,传输距离15米左右。这是目前车载诊断的主流。
- 1000BASE-T1:同样是单对线,速率升到1Gbps。主要用于ADAS和域控制器之间的高速通信。
- 100BASE-TX:两对线,就是咱们电脑上常见的那个。车上用得少,因为线束成本高。
你想想看,为什么车上不用标准RJ45?因为车规要求线束轻、抗干扰强、还得能过EMC测试。100BASE-T1只用一对线,既省了重量,又减少了电磁辐射。我在项目中遇到过一个问题——某次用普通网线做DoIP测试,结果在发动机启动瞬间通信中断。后来换成车规级100BASE-T1线缆,问题就解决了。嗯,这里要注意:物理层选型直接影响DoIP的稳定性。
关键参数对比:
| 参数 | 100BASE-T1 | 100BASE-TX |
|---|---|---|
| 线对数 | 1对 | 2对 |
| 最大距离 | 15m | 100m |
| 车规适用性 | 高 | 低 |
| EMC性能 | 优秀 | 一般 |
2.2 TCP/UDP在DoIP中的应用:选哪个?怎么用?
DoIP在传输层用了两种协议:TCP和UDP。它们分工明确,各管一摊。
UDP:用于发现和控制
DoIP的车辆发现、路由激活这些操作,走的是UDP。为什么?因为UDP无连接、速度快。你想想看,诊断仪刚接上OBD口,得先广播一个“谁在?”的报文。如果用TCP,得先建立连接,太慢了。UDP一发,所有ECU都能收到。
我曾经在调试时发现,某些ECU对UDP广播的响应时间超过500ms。后来查出来是物理层PHY芯片的节能模式没关。嗯,这里有个避坑指南:DoIP的UDP端口是13400,别搞错。
TCP:用于诊断数据
真正的诊断请求和响应(比如UDS的0x22读数据、0x2E写数据),走的是TCP。因为诊断数据需要可靠传输,不能丢包。TCP的确认重传机制保证了这一点。
我个人习惯在DoIP的TCP连接上设置一个超时时间。标准建议是5秒无活动就断开。但我在项目中遇到过一个问题——某些诊断仪在发送长报文时,中间会有几秒的思考时间。如果超时设得太短,连接就被断开了。后来我把超时放宽到10秒,问题解决。
小技巧:DoIP的TCP连接可以同时存在多个。但标准建议最多同时处理8个诊断会话。多了容易把ECU的CPU占满。
2.3 DoIP报文格式解析:从头拆到尾
DoIP的报文格式,说白了就是一套固定的头结构。不管你是发诊断请求还是做路由激活,都得按这个格式来。
标准DoIP报文头长这样:
字节偏移 字段名 长度(字节) 说明
0 协议版本 1 当前固定为0x02
1 反向协议版本 1 取反,用于校验
2-3 负载类型 2 标识报文用途
4-7 负载长度 4 后续数据的字节数
8+ 负载 可变 具体数据内容
我刚开始学DoIP时,总觉得这个头太简单了。后来才发现,简单的东西反而容易出错。举个例子:负载类型字段,它决定了后面跟的是什么数据。
常见的负载类型值:
| 负载类型值(十六进制) | 含义 | 方向 |
|---|---|---|
| 0x0001 | 车辆识别请求 | 诊断仪→ECU |
| 0x0002 | 车辆识别响应 | ECU→诊断仪 |
| 0x0003 | 路由激活请求 | 诊断仪→ECU |
| 0x0004 | 路由激活响应 | ECU→诊断仪 |
| 0x8001 | 诊断报文(UDS) | 双向 |
我记得有一次,客户反馈说DoIP连接总是失败。我抓包一看,发现诊断仪发的路由激活请求里,负载类型写成了0x0005。标准里根本没有这个值,ECU直接丢弃了。嗯,这里要注意:负载类型必须严格按照ISO 13400-2定义来填。
再来看一个实际的路由激活请求报文:
02 FD 00 03 00 00 00 0A // DoIP头:版本0x02,反向0xFD,类型0x0003,长度10字节
0E 10 00 00 00 00 00 00 // 负载:源地址0x0E10,保留8字节
00 00 00 00 // 继续保留
这个报文的意思是:诊断仪(源地址0x0E10)请求激活路由。ECU收到后,会检查源地址是否合法。如果合法,就返回一个路由激活响应,里面包含一个激活类型字段。
警告:DoIP报文头里的“反向协议版本”字段,很多人会忽略。它的作用是快速校验。如果这个字段的值不等于(0xFF - 协议版本),接收方可以直接丢弃报文,不用再解析后面的内容。我在项目中就用这个特性做过一个快速过滤,效果不错。
最后,聊一下负载长度字段。它是个4字节的无符号整数,最大能表示4GB的数据。但实际中,单个DoIP报文很少超过64KB。因为TCP的MSS(最大分段大小)通常只有1460字节。如果你要发一个大的诊断数据(比如刷写固件),得自己分片。
我个人习惯在DoIP实现中,把接收缓冲区设为64KB。这样既能处理大部分场景,又不会占用太多内存。如果你要支持刷写,建议用流式传输,不要一次性把整个固件加载到内存里。
好了,这一章的内容就到这儿。物理层选型、TCP/UDP分工、报文格式解析,这三块是DoIP的基石。下一章我们会聊SOME/IP,到时候你会发现,这两个协议其实有很多互补的地方。