4、DoIP协议详解:基于IP的诊断通信,车辆发现、路由激活与诊断连接管理

各位同学,今天我们聊DoIP。这个协议的全称是Diagnostic over Internet Protocol,说白了就是「把诊断报文塞进IP网络里跑」。你想想看,以前做诊断,要么用CAN线一根一根接,要么用K线慢慢磨。现在好了,以太网一铺,诊断数据直接在IP网络上飞,速度从几十Kbps直接跳到100Mbps甚至更高。

我个人习惯把DoIP理解成「诊断界的TCP/IP」。它底层走TCP或UDP,上层封装的是ISO 14229定义的诊断服务。嗯,这里要注意:DoIP不是重新发明了一套诊断协议,它只是换了个传输通道。

核心要点:DoIP = 诊断应用(UDS) + 传输层(TCP/UDP) + 网络层(IPv4/IPv6) + 数据链路层(以太网)。每一层各司其职,但最关键的还是那三个机制:车辆发现、路由激活、连接管理。

4.1 车辆发现:怎么在网络上找到一辆车?

你在车间里插上诊断仪,怎么知道周围有哪些车?总不能一辆一辆去敲窗户吧?DoIP用了一个很聪明的办法——广播。

具体流程是这样的:

  • 诊断仪(也就是测试设备)往网络里发一个UDP广播报文,目标端口是UDP 13400。
  • 这个报文叫车辆识别请求(Vehicle Identification Request)
  • 所有支持DoIP的ECU收到后,会回复一个车辆识别响应(Vehicle Identification Response)

响应报文里带了什么?我列一下:

字段 说明
VIN 车辆识别码,17位,全球唯一
Logical Address ECU的逻辑地址,比如网关是0x0001
EID ECU的实体ID,通常是MAC地址
GID 组ID,用于分组管理
Further Action Required 是否需要进一步操作,比如路由激活

我在项目中遇到过一个问题:某次在产线上,诊断仪发了广播,结果收到了十几个响应。一查,原来是多个ECU都配置了DoIP,而且VIN还一样——因为还没刷写。嗯,这其实是个坑,后面我们会讲怎么处理。

避坑指南:我曾经在调试时发现,某些交换机不支持UDP广播转发。结果诊断仪和ECU明明在同一个物理网络里,就是互相找不到。后来我改用单播+组播的方式才搞定。所以,如果你的网络环境比较复杂,建议同时支持广播和组播两种发现方式。

4.2 路由激活:找到车之后,怎么建立连接?

车辆发现了,接下来要干什么?你得告诉ECU:「嘿,我要开始诊断你了。」这个过程就叫路由激活(Routing Activation)

路由激活走的是TCP连接。为什么用TCP?因为诊断报文不能丢,必须可靠。UDP只管发不管到,万一丢了诊断响应,ECU可能就卡在某个状态出不来了。

流程是这样的:

  1. 诊断仪向ECU的TCP 13400端口发起连接。
  2. 连接建立后,诊断仪发送路由激活请求(Routing Activation Request)
  3. ECU回复路由激活响应(Routing Activation Response),里面带一个激活状态码。

路由激活请求里最重要的字段是源地址(Source Address)。这个地址是诊断仪的逻辑地址,比如0x0E80。ECU收到后会检查这个地址是否合法,是否已经被其他连接占用了。

注意:一个ECU同一时间只能有一个激活的路由。如果你开了两个诊断仪同时连同一个ECU,第二个会被拒绝。我见过有人踩过这个坑——产线上两台电脑同时刷写,结果一台成功一台失败,排查了半天才发现是路由冲突。

路由激活的状态码有哪些?我整理了一下:

状态码 含义
0x00 成功激活
0x01 拒绝,原因未知
0x02 拒绝,源地址已存在
0x03 拒绝,VIN不匹配
0x04 拒绝,连接数超限
0x05-0xFF 预留或制造商自定义

你想想看,如果收到0x03,说明你连错车了。VIN不匹配,可能是广播发现时拿到了旧数据,或者网络里有多辆车。这时候诊断仪应该重新做一次车辆发现。

4.3 诊断连接管理:连接建立后,怎么维持和释放?

路由激活成功后,诊断仪和ECU之间就建立了一条「诊断通道」。接下来就可以发UDS报文了。但问题来了:这条通道怎么维护?什么时候该断开?

DoIP定义了一套连接管理机制,核心是保活(Keep Alive)超时断开

具体规则:

  • 诊断仪需要定期发送Alive Check请求,告诉ECU「我还活着」。
  • ECU如果在2秒内没收到任何报文(包括诊断请求和Alive Check),就会认为连接已断开,自动释放路由。
  • 诊断仪如果发现ECU没响应,也应该主动关闭TCP连接,重新走一遍发现和激活流程。

我个人习惯把超时时间设得稍微宽松一点,比如3秒。为什么?因为有些诊断服务执行时间比较长,比如刷写一个大的Flash块,ECU可能忙着擦除,暂时顾不上回Alive Check。如果超时设得太短,就会误判。

经验之谈:我曾经在OTA升级时遇到过一个问题——刷写到一半,诊断仪突然断开了。查日志发现,ECU在擦除Flash时花了2.5秒,刚好超过了2秒的保活超时。诊断仪以为ECU挂了,主动关了连接。后来我把超时改成3秒,问题就解决了。所以,超时参数一定要根据实际场景调整,别死磕标准值。

连接管理还有一个重要概念——并发连接数。DoIP标准规定,一个ECU最多支持多少个并发TCP连接?答案是:至少1个,但具体数量由制造商决定。一般来说,网关ECU会支持2-4个,普通ECU可能只支持1个。

如果连接数满了,新来的连接会被拒绝。ECU会返回一个0x04状态码。这时候诊断仪应该怎么做?我建议先断开一个不用的连接,再重试。别傻等,等也没用。

4.4 代码示例:一个简单的DoIP路由激活实现

说了这么多理论,咱们来点实际的。下面是一个简化版的DoIP路由激活代码,用Python写的。你可以在自己的开发板上跑跑看。

import socket
import struct

# 构造路由激活请求
def build_routing_activation_request(source_address):
    # DoIP头部:版本(1字节) + 逆版本(1字节) + 载荷类型(2字节) + 载荷长度(4字节)
    # 路由激活的载荷类型是0x0005
    # 载荷内容:源地址(2字节) + 保留(2字节)
    doip_header = struct.pack('!BBHI', 0x02, 0xFD, 0x0005, 4)
    payload = struct.pack('!HH', source_address, 0x0000)
    return doip_header + payload

# 解析路由激活响应
def parse_routing_activation_response(data):
    # 跳过DoIP头部(8字节),取第8字节开始的状态码
    if len(data) < 9:
        return None
    status = data[8]
    return status

# 主流程
def main():
    # 假设ECU的IP是192.168.1.100
    ecu_ip = '192.168.1.100'
    ecu_port = 13400
    source_addr = 0x0E80  # 诊断仪的逻辑地址

    # 建立TCP连接
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(5.0)
    try:
        sock.connect((ecu_ip, ecu_port))
        print('TCP连接建立成功')
    except Exception as e:
        print('连接失败:', e)
        return

    # 发送路由激活请求
    req = build_routing_activation_request(source_addr)
    sock.sendall(req)
    print('路由激活请求已发送')

    # 接收响应
    try:
        resp = sock.recv(1024)
        status = parse_routing_activation_response(resp)
        if status == 0x00:
            print('路由激活成功!可以开始诊断了')
        else:
            print('路由激活失败,状态码: 0x{:02X}'.format(status))
    except Exception as e:
        print('接收响应失败:', e)

    sock.close()

if __name__ == '__main__':
    main()

提示:这段代码只是演示用,生产环境里你还需要处理粘包、重传、超时重试等逻辑。另外,DoIP头部里的版本字段,0x02代表ISO 13400-2:2012版本,0x03代表2019版本。我建议你用0x03,兼容性更好。

4.5 总结一下

DoIP的车辆发现、路由激活和连接管理,说白了就是三个步骤:

  • 发现:用UDP广播找到车,拿到VIN和逻辑地址。
  • 激活:用TCP建立诊断通道,告诉ECU「我要开始干活了」。
  • 管理:用保活机制维持连接,超时自动释放,避免资源浪费。

你想想看,这套机制是不是很像我们平时用手机连Wi-Fi?先扫描(发现),再输入密码(激活),然后一直连着(管理)。只不过DoIP的「密码」是逻辑地址和VIN,而且不需要你手动输入。

下一章我们会讲DoIP的报文格式和诊断数据传输,到时候会涉及更多细节。嗯,今天就到这里,有问题随时问我。