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可能就卡在某个状态出不来了。
流程是这样的:
- 诊断仪向ECU的TCP 13400端口发起连接。
- 连接建立后,诊断仪发送路由激活请求(Routing Activation Request)。
- 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的报文格式和诊断数据传输,到时候会涉及更多细节。嗯,今天就到这里,有问题随时问我。