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,到时候你会发现,这两个协议其实有很多互补的地方。