第2章:DoIP协议栈架构——OSI七层模型与DoIP的映射关系

各位同学,咱们今天聊聊DoIP的协议栈架构。说实话,我刚接触DoIP那会儿,也被这七层模型搞得有点晕。但后来我发现,只要把OSI模型和DoIP的映射关系理清楚,后面学起来就顺多了。

你想想看,DoIP说白了就是把传统的CAN诊断搬到了以太网上。那以太网本身就有自己的分层结构,DoIP只是在这个基础上加了诊断相关的功能。嗯,咱们一层一层来看。

2.1 OSI七层模型回顾

先简单回顾一下OSI模型。这玩意儿是国际标准化组织定义的,从上到下分别是:

OSI层 功能简述 DoIP中的角色
应用层(第7层) 用户接口、应用协议 UDS诊断服务
表示层(第6层) 数据格式转换、加密 DoIP消息编码
会话层(第5层) 会话管理、同步 DoIP会话控制
传输层(第4层) 端到端可靠传输 TCP/UDP
网络层(第3层) 路由、寻址 IP协议
数据链路层(第2层) 帧封装、MAC寻址 以太网MAC
物理层(第1层) 比特流传输 以太网PHY

我记得有次在项目评审会上,一个刚入行的同事问我:“老师,DoIP是不是只用到了传输层以上的部分?”我当时就笑了——其实每一层都跑不掉,少了哪一层,诊断数据都传不过去。

2.2 DoIP协议栈各层功能详解

2.2.1 物理层(第1层)

物理层负责把比特流变成电信号或者光信号。在DoIP里,我们通常用的是100BASE-TX或1000BASE-T,也就是百兆或千兆以太网。

我个人习惯用100BASE-TX做测试,因为成本低,而且大多数OBD接口都支持。但要注意,有些高端车型已经开始用1000BASE-T了,特别是那些需要传输大量数据的场景。

注意:物理层虽然看起来简单,但线缆质量、接头接触不良都会导致DoIP连接不稳定。我曾经遇到过一台车,怎么都连不上DoIP,最后发现是OBD接口的针脚氧化了。

2.2.2 数据链路层(第2层)

数据链路层负责把数据封装成帧,加上MAC地址。DoIP在这里用的是标准的以太网MAC帧格式。

你想想看,一辆车上可能有几十个ECU,每个ECU都有自己的MAC地址。DoIP测试设备需要知道目标ECU的MAC地址,才能把数据发过去。

这里有个小技巧:DoIP协议规定了一个特殊的MAC地址范围,用于诊断通信。我建议你在抓包的时候,直接过滤这个范围的MAC地址,能省不少事。

2.2.3 网络层(第3层)

网络层负责IP寻址和路由。DoIP用的是IPv4或IPv6,目前主流还是IPv4。

我记得在做一个项目时,发现DoIP测试设备总是连不上某个ECU。查了半天,原来是IP地址冲突了。那个ECU的IP地址和另一个设备重复了,导致数据包乱跑。

经验之谈:DoIP测试环境里,我建议给每个ECU分配静态IP地址,或者用DHCP但绑定MAC地址。这样能避免很多莫名其妙的连接问题。

2.2.4 传输层(第4层)

传输层是DoIP的核心。它用了两个协议:TCP和UDP。

  • TCP:用于诊断数据的可靠传输。DoIP的激活、诊断请求/响应都用TCP。
  • UDP:用于发现和广播。比如DoIP实体发现、车辆发现这些。

说白了,TCP就像快递,保证包裹送到;UDP就像广播喇叭,喊一嗓子,谁听到谁回应。

DoIP标准里定义了几个端口号:

端口 协议 用途
13400 TCP DoIP诊断数据
13400 UDP DoIP发现服务

嗯,这里要注意,TCP和UDP用的是同一个端口号,但用途完全不同。我在测试时经常用Wireshark过滤tcp.port == 13400udp.port == 13400来分别查看。

2.2.5 会话层(第5层)

会话层管理DoIP的会话状态。DoIP定义了三种会话:

  • 默认会话:刚建立连接时的状态,只能做基本操作。
  • 扩展会话:可以执行诊断服务,比如读取故障码。
  • 编程会话:用于刷写ECU固件,权限最高。

我刚开始做DoIP测试时,经常忘记切换会话,结果诊断请求发出去,ECU直接返回“服务不支持”。后来养成了习惯,每次发诊断请求前先确认会话状态。

核心要点:会话层是DoIP安全机制的一部分。没有正确的会话状态,很多诊断服务是执行不了的。

2.2.6 表示层(第6层)

表示层负责数据格式的转换。在DoIP里,主要是把UDS诊断消息封装成DoIP消息格式。

DoIP消息的头部结构是这样的:

| 协议版本 (1字节) | 反向协议版本 (1字节) | 负载类型 (2字节) | 负载长度 (4字节) | 负载数据 (可变) |

举个例子,一个简单的诊断请求“读取VIN码”,在表示层会被封装成DoIP消息,然后交给传输层发送。

我个人习惯用Python写个小脚本,手动构造DoIP消息来测试。这样能更清楚地理解表示层的工作。

2.2.7 应用层(第7层)

应用层就是UDS诊断服务了。DoIP本身不定义诊断服务,它只负责传输。真正的诊断逻辑在UDS里。

常见的UDS服务有:

  • 0x10:诊断会话控制
  • 0x22:读取数据标识符
  • 0x2E:写入数据标识符
  • 0x19:读取故障码
  • 0x14:清除故障码

你想想看,DoIP就像一条高速公路,UDS就是路上跑的车。路修得再好,车上没货也是白搭。

2.3 各层之间的协作关系

说了这么多,咱们来看看这些层是怎么协作的。假设你要读取ECU的VIN码:

  1. 应用层:构造UDS请求0x22,参数是VIN码的DID。
  2. 表示层:把UDS请求封装成DoIP消息,设置负载类型为“诊断消息”。
  3. 会话层:确认当前会话状态是扩展会话,允许执行诊断服务。
  4. 传输层:把DoIP消息通过TCP发送出去。
  5. 网络层:加上IP头部,指定源和目标IP地址。
  6. 数据链路层:加上MAC头部,指定源和目标MAC地址。
  7. 物理层:把数据变成电信号,通过网线发送。

ECU收到后,反向解析,最终返回VIN码。整个过程看起来复杂,但实际在硬件里,几百微秒就完成了。

我的建议:刚开始学DoIP,不要急着看代码。先把这七层模型搞清楚,每层做什么、怎么协作,心里有个谱。后面写代码、抓包分析的时候,你就知道该看哪一层了。

2.4 常见问题与避坑指南

最后,分享几个我在项目中踩过的坑:

  • 物理层问题:网线太长或质量差,导致信号衰减。我曾经用了一根10米的劣质网线,结果DoIP连接时断时续。
  • IP地址冲突:多个ECU用了同一个IP,导致数据包乱跑。建议用DHCP加MAC绑定。
  • 端口被占用:13400端口被其他程序占用了,导致DoIP服务起不来。用netstat -an | grep 13400检查一下。
  • 会话状态不对:在默认会话下发诊断请求,ECU直接拒绝。记得先切换到扩展会话。

嗯,今天就先聊到这儿。下一章咱们会讲DoIP的报文格式,到时候我会带大家手动解析一个DoIP消息,看看每一层的数据长什么样。

记住,搞DoIP测试,七层模型是基本功。把这层关系理清了,后面学什么都快。