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                   |
注意: 路由激活成功后,车辆会为这个诊断仪分配一个TCP套接字。每个诊断仪只能有一个激活的路由。如果另一个诊断仪也想激活,车辆会拒绝。

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连接建立时,一定要做好日志记录。把每个步骤的请求、响应、时间戳都记下来。这样出了问题,一看日志就知道是哪一步卡住了。别问我为什么知道,我曾经为了一个连接超时的问题,熬了三个通宵才找到原因。