4、DoIP连接建立:车辆发现与声明、路由激活过程、连接建立时序分析
好,咱们今天聊聊DoIP连接建立这件事。说实话,很多刚接触车载以太网的工程师,都觉得DoIP不就是个IP连接嘛,能有多复杂?但我在项目里踩过坑之后才发现,这里面的门道还真不少。
DoIP连接建立,说白了就是三个步骤:车辆发现、路由激活、连接建立。每一步都有它存在的道理,咱们一个一个拆开看。
4.1 车辆发现与声明
你想想看,诊断仪插到车上,它怎么知道这辆车支持DoIP?怎么知道它的IP地址?这就是车辆发现机制要解决的问题。
DoIP车辆发现有两种方式:
- 主动发现:诊断仪发送广播请求,车辆回复
- 被动声明:车辆主动广播自己的存在
我个人习惯把这两种方式结合起来用。主动发现适合刚上电的场景,被动声明适合车辆状态变化时通知诊断仪。
4.1.1 车辆声明消息(VAM)
车辆上电后,会周期性地发送VAM(Vehicle Announcement Message)。这个周期一般是1秒到3秒,具体看OEM怎么定义。我在一个项目中遇到过,某款车的VAM周期设成了5秒,结果诊断仪老是发现不了车辆,排查了半天才发现是周期太长。
VAM的格式是这样的:
| 字节偏移 | 字段名 | 长度 | 说明 |
|----------|---------------------|------|------------------------------|
| 0 | 协议版本 | 1 | 0x02 表示ISO 13400-2:2012 |
| 1 | 负载类型 | 1 | 0x04 表示车辆声明消息 |
| 2-3 | 负载长度 | 2 | 固定为0x0020 |
| 4-5 | VIN(高16位) | 2 | VIN码的前两个字节 |
| 6-11 | VIN(剩余部分) | 6 | VIN码的剩余字节 |
| 12-15 | 逻辑地址 | 4 | 车辆的DoIP逻辑地址 |
| 16-19 | EID | 4 | 实体标识符 |
| 20-23 | GID | 4 | 组标识符 |
| 24-27 | 进一步动作标志 | 4 | 0x00000000 表示无进一步动作 |
| 28-31 | 保留 | 4 | 填充0x00000000 |
嗯,这里要注意:VAM中的VIN字段只占8个字节,但标准VIN是17个字符。所以实际传输时,VIN会被截断或者用其他方式编码。我建议你在实现时,仔细核对OEM的规范,别在这个细节上翻车。
4.1.2 车辆发现请求与响应
诊断仪发送的车辆发现请求,用的是UDP广播。目标端口是UDP 13400,目标地址是255.255.255.255。
请求格式:
| 字节偏移 | 字段名 | 长度 | 说明 |
|----------|-------------|------|------------------------------|
| 0 | 协议版本 | 1 | 0x02 |
| 1 | 负载类型 | 1 | 0x01 表示车辆发现请求 |
| 2-3 | 负载长度 | 2 | 0x0000 |
车辆收到请求后,会回复一个车辆发现响应:
| 字节偏移 | 字段名 | 长度 | 说明 |
|----------|-------------|------|------------------------------|
| 0 | 协议版本 | 1 | 0x02 |
| 1 | 负载类型 | 1 | 0x02 表示车辆发现响应 |
| 2-3 | 负载长度 | 2 | 0x0010 |
| 4-7 | 逻辑地址 | 4 | 车辆的DoIP逻辑地址 |
| 8-11 | EID | 4 | 实体标识符 |
| 12-15 | GID | 4 | 组标识符 |
| 16-19 | 进一步动作 | 4 | 0x00000000 |
4.2 路由激活过程
车辆发现之后,诊断仪知道了车辆的存在。但这时候还不能直接发诊断请求,得先激活路由。为什么?说白了,就是告诉车辆:我要开始诊断了,你给我开个通道。
路由激活用的是TCP连接。诊断仪先和车辆建立TCP连接,然后发送路由激活请求。
4.2.1 路由激活请求
| 字节偏移 | 字段名 | 长度 | 说明 |
|----------|-----------------|------|------------------------------|
| 0 | 协议版本 | 1 | 0x02 |
| 1 | 负载类型 | 1 | 0x05 表示路由激活请求 |
| 2-3 | 负载长度 | 2 | 0x000C |
| 4-7 | 源逻辑地址 | 4 | 诊断仪的逻辑地址 |
| 8-11 | 保留 | 4 | 0x00000000 |
| 12-15 | 保留 | 4 | 0x00000000 |
这里有个关键点:源逻辑地址。诊断仪必须使用一个合法的逻辑地址,这个地址通常由OEM分配。我曾经见过一个案例,诊断仪用了0x0000作为源逻辑地址,结果车辆直接拒绝了路由激活请求。
4.2.2 路由激活响应
车辆收到请求后,会检查源逻辑地址是否合法。如果合法,就回复一个激活成功的响应:
| 字节偏移 | 字段名 | 长度 | 说明 |
|----------|-----------------|------|------------------------------|
| 0 | 协议版本 | 1 | 0x02 |
| 1 | 负载类型 | 1 | 0x06 表示路由激活响应 |
| 2-3 | 负载长度 | 2 | 0x0010 |
| 4-7 | 客户端逻辑地址 | 4 | 诊断仪的逻辑地址 |
| 8-11 | 套接字类型 | 4 | 0x00000001 表示TCP套接字 |
| 12-15 | 保留 | 4 | 0x00000000 |
| 16-19 | 保留 | 4 | 0x00000000 |
如果源逻辑地址不合法,车辆会回复一个错误响应:
| 字节偏移 | 字段名 | 长度 | 说明 |
|----------|-----------------|------|------------------------------|
| 0 | 协议版本 | 1 | 0x02 |
| 1 | 负载类型 | 1 | 0x06 表示路由激活响应 |
| 2-3 | 负载长度 | 2 | 0x0010 |
| 4-7 | 客户端逻辑地址 | 4 | 0x00000000 |
| 8-11 | 响应码 | 4 | 0x00000001 表示地址冲突 |
| 12-15 | 保留 | 4 | 0x00000000 |
| 16-19 | 保留 | 4 | 0x00000000 |
4.3 连接建立时序分析
好,现在我们把整个连接建立过程串起来,看看时序是什么样的。
我画了一个典型的时序图,你感受一下:
诊断仪 车辆
| |
|--- UDP广播: 车辆发现请求 ---->|
| |
|<--- UDP单播: 车辆发现响应 ----|
| |
|--- TCP连接建立 (三次握手) --->|
|<--- TCP连接建立确认 ---------|
| |
|--- TCP: 路由激活请求 -------->|
|<--- TCP: 路由激活响应 --------|
| |
|--- TCP: 诊断请求 ------------>|
|<--- TCP: 诊断响应 ------------|
| |
这个时序看起来简单,但实际项目中,每个环节都可能出问题。我遇到过几个典型问题:
- UDP广播被防火墙拦截:有些车载网关会过滤UDP广播,导致车辆发现失败。解决办法是在网关配置中开放UDP 13400端口。
- TCP连接超时:车辆端TCP连接建立后,如果诊断仪迟迟不发路由激活请求,车辆会超时断开连接。超时时间一般是5秒到10秒。
- 路由激活冲突:两个诊断仪同时尝试激活路由,车辆只会响应第一个。第二个会收到地址冲突的错误。
- 车辆发现用UDP广播,端口13400
- 路由激活用TCP,必须先建立TCP连接
- 每个诊断仪只能有一个激活的路由
- 源逻辑地址必须合法,否则路由激活失败
嗯,说到这儿,我想起一个项目中的教训。当时我们做DoIP网关,发现诊断仪总是连不上。排查了半天,发现是车辆端的TCP backlog设得太小,导致并发连接被拒绝。后来把backlog从5改到10,问题就解决了。你想想看,这种细节,文档里根本不会写,全靠实战积累。
最后,我建议你在做DoIP连接建立时,一定要做好日志记录。把每个步骤的请求、响应、时间戳都记下来。这样出了问题,一看日志就知道是哪一步卡住了。别问我为什么知道,我曾经为了一个连接超时的问题,熬了三个通宵才找到原因。