DoIP车载以太网诊断实战:从车辆发现到路由激活与UDS诊断
简介这是一份面向汽车电子诊断工程师、车载网络测试人员及智能网联汽车方向学习者的DoIP基础知识演示文稿系统梳理了基于以太网与IP协议的车辆诊断技术框架帮助读者建立从协议原理到诊断流程的完整认知。资源包内含1个pptx文件大小约5.11MB以图文幻灯片形式呈现便于课堂讲解与自学查阅。内容围绕DoIP应用领域、TCP与UDP协议需求、CDD与ODX描述文件、诊断测试器与逻辑地址、诊断网关、车辆发现、车辆公告与识别、IP地址配置、路由激活及DoIP消息等核心模块展开并配有车辆发现、路由激活序列、诊断请求响应等示例说明可帮助读者理解诊断通道建立与数据路由的关键环节。目前已有110人学习适合作为DoIP入门与协议梳理的参考材料。1. 从一份 2018 年的 DoIP 课件说起以太网诊断到底怎么落地手里这份ETH_01_DoIP_Basics.pptx是 V11.0.01 版本、2018-09-07 出的配套 CANoe/CANalyzer.Ethernet2 环境。它不是那种翻两页就扔的科普 PPT而是把 DoIP 从车辆发现、路由激活到诊断报文这条链路完整串了一遍的培训材料。如果你正在做车载以太网诊断或者被 13400 端口、逻辑地址、激活线这些词绕得头晕这份课件能帮你把「诊断仪怎么找到车、怎么建立通道、怎么发第一条诊断请求」这条主线理清楚。它适合两类人一是刚接触 DoIP、需要一份能对着抓包看的入门材料的新手二是已经在用 CANoe 做以太网诊断、但想回头补协议细节的老手。下面我按课件的章节顺序把每一页背后的操作逻辑拆开讲。2. DoIP 协议栈与诊断节点从 TCP/UDP 分工到逻辑地址分配2.1 为什么 DoIP 要同时用 TCP 和 UDP课件第 3 页到第 5 页反复强调一件事DoIP 不是单一协议它跑在 TCP 和 UDP 两套传输层之上。UDP 负责状态信息、配置广播这类「发了就完、不保证到达」的活TCP 负责诊断消息、存活检查这类「必须可靠、顺序不能乱」的活。这个分工不是随便定的背后是诊断场景的两种需求车辆发现阶段诊断仪需要广播询问「附近有没有车」用 UDP 最省事一旦进入诊断会话读写故障码、刷写程序丢一个包都可能让 ECU 进入异常状态必须走 TCP。端口方面课件给的例子是 13400这是 DoIP 的默认端口。实际项目里我见过不少主机厂把 UDP 和 TCP 都绑在 13400 上也有分开用 13400 和 13401 的。如果你在 CANoe 里配 DoIP 节点第一步就是确认这两个端口和 ECU 侧一致否则连车辆公告都收不到。典型 DoIP 节点网络配置 诊断仪 IP: 192.168.1.10 诊断网关 IP: 192.168.1.20 UDP 端口: 13400 TCP 端口: 13400上面这个配置来自课件第 5 页和第 6 页的示例。注意逻辑地址和 IP 地址是两码事IP 地址定位到「哪台设备」逻辑地址定位到「哪条诊断通道」。课件里明确写了每个 ECU 和每个诊断仪都要有自己独立的逻辑地址。我一般会在项目初期就拉一张表把 Tester、Gateway、ECU 的逻辑地址列清楚不然后面路由激活请求里填错一个字节网关直接回 NACK排查起来很费时间。2.2 诊断仪、网关、ECU 三者的角色划分课件第 4 页到第 6 页把 DoIP 网络里的节点分成三类诊断测试器Tester、诊断网关Gateway、电子控制单元ECU。Tester 是发起方需要加载 CDD 或 ODX 描述文件知道每个 ECU 支持哪些诊断服务。Gateway 是桥梁一边连着外部以太网一边连着车内网络负责把 Tester 的请求路由到正确的 ECU。ECU 是最终执行诊断服务的目标。这里有个容易忽略的点课件提到 Gateway 上有一条「激活线」通常通过 WWH-OBD 连接器物理激活。也就是说不是插上网线就能诊断有些车型需要先给网关一个物理信号诊断接口才真正打开。我遇到过一种情况Tester 发车辆识别请求UDP 包能发出去但收不到任何响应查了半天发现是激活线没接。所以如果你在台架上复现先确认网关的诊断使能条件是否满足。描述文件方面CDD 和 ODX 是两种常见格式。CDD 偏 CANoe 生态ODX 是 ISO 22901 标准。课件没有展开讲文件结构但实际用的时候Tester 端加载的描述文件必须和 ECU 侧实际实现的诊断服务一致否则你会看到请求发出去了ECU 回一个「服务不支持」的否定响应。2.3 逻辑地址与 IP 地址的映射关系课件第 5 页和第 6 页都提到了逻辑地址的定义。逻辑地址是 2 字节通常高字节表示地址类型低字节表示具体节点。比如 0x0E00 到 0x0EFF 一般分配给外部测试设备0x0001 到 0x00FF 分配给车内 ECU。这个划分不是强制的但很多项目会遵循这个惯例。在 CANoe 里配置 DoIP 节点时你需要填三样东西IP 地址、逻辑地址、端口号。这三者组合起来才能唯一标识一条诊断链路。我一般会建议在项目里维护一张这样的表节点IP 地址逻辑地址UDP 端口TCP 端口诊断仪192.168.1.100x0E001340013400网关192.168.1.200x00011340013400发动机 ECU车内网络0x0010--车身 ECU车内网络0x0020--这张表在排查路由激活失败时特别有用。比如网关回 NACK 代码 0x03未知目标地址你对着表一看就知道是逻辑地址填错了还是 ECU 根本没注册。3. 车辆发现与路由激活从 UDP 广播到 TCP 通道建立3.1 车辆公告和车辆识别请求的区别课件第 8 页到第 12 页专门讲了车辆发现。这里有两个容易混淆的概念车辆公告Vehicle Announcement和车辆识别请求/响应Vehicle Identification Request/Response。车辆公告是车辆主动发出的通常在上电后或收到特定触发条件时通过 UDP 广播或组播发送。公告里包含 VIN 和 EID实体识别号有时还有逻辑地址。诊断仪如果被动监听收到公告就知道「附近有车」。车辆识别请求是诊断仪主动发出的。诊断仪不知道车辆 IP 时可以发广播知道 IP 时可以发单播。车辆收到请求后回一个车辆识别响应内容比公告更详细包括 VIN、EID、逻辑地址、进一步通信需要的 IP 和端口。课件第 11 页和第 12 页给了两个示例一个是车辆公告的报文结构一个是车辆识别响应的报文结构。实际抓包时你可以用 Wireshark 过滤udp.port 13400看有没有这些报文。如果发了识别请求但没响应先检查 IP 地址是不是在同一网段再检查防火墙有没有拦 UDP。3.2 路由激活的完整序列课件第 13 页到第 16 页是路由激活部分这是 DoIP 里最关键的一步。流程大致是诊断仪先和网关建立 TCP 连接然后发路由激活请求网关回路由激活响应成功后诊断通道才算建立。路由激活请求里包含诊断仪的逻辑地址、激活类型、以及可选的认证信息。激活类型常见的有 0x00默认、0x01WWH-OBD、0xE0中央安全。课件第 14 页的示例 3 展示了完整的 TCP 连接建立和路由激活序列。路由激活请求报文示例十六进制 02 FD 00 05 00 00 00 07 0E 00 00 00 00 00 00 00 | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | └─ 可选认证数据 | | | | | | | | | | | | | | └──── 激活类型 | | | | | | | | | | | | └────────── 诊断仪逻辑地址 | | | | | | | | | | | └───────────── 保留 | | | | | | | | | | └──────────────── 源地址 | | | | | | | | | └─────────────────── 报文长度 | | | | | | | | └────────────────────── 报文类型 | | | | | | | └───────────────────────── 协议版本 | | | | | | └──────────────────────────── 协议版本反码 | | | | | └─────────────────────────────── 报文类型 | | | | └────────────────────────────────── 报文长度 | | | └───────────────────────────────────── 源地址 | | └──────────────────────────────────────── 目标地址 | └─────────────────────────────────────────── 协议版本 └────────────────────────────────────────────── 协议版本反码上面这个结构是 DoIP 通用报文头加路由激活请求的典型布局。实际解析时重点看报文类型字段0x0005 是路由激活请求0x0006 是路由激活响应。响应里的代码 0x10 表示成功其他值都是失败原因。我见过 0x02已有 TCP 连接、0x03未知目标地址、0x04所有通道被占用这几种 NACK排查思路各不相同。3.3 TCP 连接建立与保活课件第 14 页提到 TCP 连接建立第 15 页讲路由激活序列。这里有个实操细节DoIP 的 TCP 连接不是普通的短连接它需要保持长连接并且有存活检查机制。诊断仪和网关之间会定期发 Alive Check 请求和响应确认对方还在线。如果你在 CANoe 里模拟诊断仪TCP 连接建立后不要急着发诊断请求先等路由激活响应。我见过有人跳过路由激活直接发诊断服务结果网关直接把 TCP 连接断了。顺序不能乱TCP 连接 → 路由激活请求 → 路由激活响应 → 诊断请求。4. 诊断请求与响应从报文结构到实际抓包排查4.1 DoIP 报文头的通用结构课件第 20 页专门讲了 DoIP 消息的报文结构。所有 DoIP 报文都遵循一个通用头协议版本、协议版本反码、报文类型、报文长度、源地址、目标地址。协议版本目前常见的是 0x02反码是 0xFD两者相加等于 0xFF用来做简单校验。报文类型决定了后面的负载怎么解析。课件里涉及的报文类型包括0x0001 车辆识别请求、0x0004 车辆公告、0x0005 路由激活请求、0x0006 路由激活响应、0x8001 诊断消息。诊断消息的负载就是 UDS 服务比如 0x22 读数据、0x2E 写数据、0x31 例程控制。# DoIP 报文头解析示例 import struct def parse_doip_header(data): # 前 8 字节是通用头 version, inverse, msg_type, length, src, dst struct.unpack(BBHIHH, data[:12]) # 校验协议版本 if version inverse ! 0xFF: raise ValueError(协议版本校验失败) return { version: version, msg_type: msg_type, length: length, src_addr: hex(src), dst_addr: hex(dst), payload: data[12:12length] } # 示例解析一个路由激活响应 raw bytes.fromhex(02FD0006000000090E00000000001000) result parse_doip_header(raw) print(result)上面这段代码演示了怎么用 Python 解析 DoIP 报文头。struct.unpack的格式字符串BBHIHH对应大端序下的字段布局。实际用的时候你可以把抓到的十六进制流丢进去看源地址和目标地址是不是符合预期。如果length字段和实际负载长度对不上说明报文被截断了检查 TCP 流有没有丢包。4.2 诊断消息的发送与接收课件第 17 页到第 19 页讲了诊断请求和响应的序列。一旦路由激活成功诊断仪就可以通过 TCP 连接发诊断消息了。诊断消息的报文类型是 0x8001负载是完整的 UDS 请求比如22 F1 90读 VIN。ECU 收到请求后回一个诊断响应报文类型也是 0x8001负载是 UDS 响应。如果 ECU 暂时不能回复会先回一个 0x8002诊断消息肯定响应或 0x8003诊断消息否定响应告诉诊断仪「收到了稍等」或「不支持」。实际抓包时我一般会在 CANoe 的 Trace 窗口里过滤doip协议然后看报文类型和源/目标地址。如果发了诊断请求但没响应先确认路由激活是否成功再确认目标逻辑地址是不是 ECU 的地址。有时候网关会把请求路由到错误的 ECU这时候要看网关的路由表配置。4.3 用 CANoe 复现课件里的示例课件里给了四个示例示例 1 车辆公告、示例 2 车辆识别、示例 3 路由激活、示例 4 诊断请求响应。如果你手头有 CANoe.Ethernet2可以按下面的步骤复现新建一个以太网配置添加 DoIP 节点。配置诊断仪的 IP 和逻辑地址配置网关的 IP 和逻辑地址。在 Simulation 里发送车辆识别请求观察有没有响应。建立 TCP 连接发送路由激活请求检查响应代码。路由激活成功后发送 UDS 诊断请求比如22 F1 90。在 Trace 窗口里看完整的报文序列。如果卡在某一步先看 CANoe 的 Write 窗口有没有报错再去 Wireshark 里对照报文结构。课件里的示例报文可以直接在 CANoe 的 Packet Builder 里构造省去手算十六进制的麻烦。5. 避坑与排查DoIP 联调中最容易翻车的五个点5.1 车辆发现阶段收不到公告现象诊断仪发车辆识别请求UDP 包发出去了但收不到任何响应。原因常见有三种——IP 地址不在同一网段、防火墙拦了 UDP 13400、网关的激活线没接。解决先用ping确认网络层通不通再用 Wireshark 看 UDP 包有没有到达网关。如果包到了但没响应检查网关的诊断使能条件比如激活线电压是否正常。5.2 路由激活返回 NACK 0x03现象TCP 连接建立成功但路由激活响应代码是 0x03。原因路由激活请求里的目标地址填错了或者网关的路由表里没有这个逻辑地址。解决对照逻辑地址分配表确认请求里的源地址和网关期望的目标地址一致。如果网关支持多通道检查通道号有没有冲突。5.3 TCP 连接频繁断开现象诊断请求发到一半TCP 连接断了诊断仪收到 RST 包。原因Alive Check 超时或者诊断仪和网关的 TCP 保活参数不匹配。解决在 CANoe 里调整 TCP Keepalive 间隔确保小于网关的超时时间。一般网关的超时是 5 秒诊断仪这边设 2 到 3 秒比较安全。5.4 诊断请求发出但 ECU 不响应现象路由激活成功诊断请求也发出去了但 ECU 没有任何响应。原因目标逻辑地址不是 ECU 的地址或者 ECU 不支持该 UDS 服务。解决先确认逻辑地址再确认 ECU 的诊断描述文件里有没有这个服务。如果服务支持但没响应检查 ECU 是不是处于默认会话有些服务需要在扩展会话下才能执行。5.5 报文长度字段与实际不符现象Wireshark 解析 DoIP 报文时报「Malformed Packet」。原因报文头的 length 字段和实际负载长度不一致通常是发送端构造报文时算错了。解决在代码里加校验发送前重新计算负载长度。如果用的是 CANoe 的 Packet Builder检查字段类型是不是选错了比如把 2 字节的长度字段填成了 1 字节。6. 进阶技巧用 Python 脚本批量验证 DoIP 节点响应课件最后一页停在 DoIP 消息结构没有展开讲自动化验证。但实际项目里我经常需要批量确认多个 ECU 的逻辑地址是否可达、诊断服务是否可用。这时候用 CANoe 手动点效率太低我一般会写一个 Python 脚本基于socket和struct直接发 DoIP 报文。import socket import struct import time def build_doip_header(msg_type, payload, src_addr, dst_addr): 构造 DoIP 通用报文头 version 0x02 inverse 0xFD length len(payload) header struct.pack(BBHIHH, version, inverse, msg_type, length, src_addr, dst_addr) return header payload def routing_activation(tester_addr, gateway_addr, activation_type0x00): 发送路由激活请求 payload struct.pack(H, activation_type) b\x00\x00\x00\x00 msg build_doip_header(0x0005, payload, tester_addr, gateway_addr) return msg def send_diagnostic(sock, tester_addr, ecu_addr, uds_request): 发送诊断请求 msg build_doip_header(0x8001, uds_request, tester_addr, ecu_addr) sock.send(msg) response sock.recv(1024) return response # 主流程 gateway_ip 192.168.1.20 gateway_port 13400 tester_addr 0x0E00 gateway_addr 0x0001 ecu_addr 0x0010 sock socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((gateway_ip, gateway_port)) # 路由激活 act_msg routing_activation(tester_addr, gateway_addr) sock.send(act_msg) act_resp sock.recv(1024) print(路由激活响应:, act_resp.hex()) # 发送 UDS 读 VIN 请求 vin_resp send_diagnostic(sock, tester_addr, ecu_addr, bytes.fromhex(22F190)) print(VIN 响应:, vin_resp.hex()) sock.close()这段脚本的核心是build_doip_header函数它把协议版本、反码、报文类型、长度、源地址、目标地址按大端序打包。routing_activation构造路由激活请求激活类型默认 0x00。send_diagnostic发送 UDS 请求并等待响应。参数方面tester_addr和gateway_addr要和你项目里的逻辑地址一致。ecu_addr是目标 ECU 的逻辑地址。如果路由激活响应不是0x10脚本会打印出来你可以对照第 5 章的排查表定位问题。我一般会把这个脚本扩展成批量测试读一个 CSV 文件里面列出所有 ECU 的逻辑地址和要测试的 UDS 服务循环发送并记录响应时间。这样在台架上跑一遍就能知道哪些节点在线、哪些服务可用。比在 CANoe 里一个个点快得多而且结果可以存档方便对比不同软件版本的诊断能力。从那以后我每次拿到新的 DoIP 节点配置都强制先跑一遍这个脚本确认路由激活和基础诊断服务没问题再进 CANoe 做详细测试。希望帮到你。本文还有配套的精品资源点击获取