第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了,特别是那些需要传输大量数据的场景。
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地址和另一个设备重复了,导致数据包乱跑。
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 == 13400或udp.port == 13400来分别查看。
2.2.5 会话层(第5层)
会话层管理DoIP的会话状态。DoIP定义了三种会话:
- 默认会话:刚建立连接时的状态,只能做基本操作。
- 扩展会话:可以执行诊断服务,比如读取故障码。
- 编程会话:用于刷写ECU固件,权限最高。
我刚开始做DoIP测试时,经常忘记切换会话,结果诊断请求发出去,ECU直接返回“服务不支持”。后来养成了习惯,每次发诊断请求前先确认会话状态。
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码:
- 应用层:构造UDS请求0x22,参数是VIN码的DID。
- 表示层:把UDS请求封装成DoIP消息,设置负载类型为“诊断消息”。
- 会话层:确认当前会话状态是扩展会话,允许执行诊断服务。
- 传输层:把DoIP消息通过TCP发送出去。
- 网络层:加上IP头部,指定源和目标IP地址。
- 数据链路层:加上MAC头部,指定源和目标MAC地址。
- 物理层:把数据变成电信号,通过网线发送。
ECU收到后,反向解析,最终返回VIN码。整个过程看起来复杂,但实际在硬件里,几百微秒就完成了。
2.4 常见问题与避坑指南
最后,分享几个我在项目中踩过的坑:
- 物理层问题:网线太长或质量差,导致信号衰减。我曾经用了一根10米的劣质网线,结果DoIP连接时断时续。
- IP地址冲突:多个ECU用了同一个IP,导致数据包乱跑。建议用DHCP加MAC绑定。
- 端口被占用:13400端口被其他程序占用了,导致DoIP服务起不来。用
netstat -an | grep 13400检查一下。 - 会话状态不对:在默认会话下发诊断请求,ECU直接拒绝。记得先切换到扩展会话。
嗯,今天就先聊到这儿。下一章咱们会讲DoIP的报文格式,到时候我会带大家手动解析一个DoIP消息,看看每一层的数据长什么样。
记住,搞DoIP测试,七层模型是基本功。把这层关系理清了,后面学什么都快。