2、网络架构基础:TCP/IP协议栈回顾、UDP与TCP在DoIP中的应用场景、以太网帧结构、IP地址与端口分配

好,咱们正式开始第二讲。在动手写DoIP协议栈之前,有一件事必须做——把网络基础打牢。你想想看,DoIP说白了就是把传统的诊断数据包,塞进以太网帧里,通过IP网络传输。如果TCP/IP协议栈的基本原理都不清楚,后面调试起来会非常痛苦。

我个人习惯是,每接触一个新协议,先花半天时间把底层网络模型过一遍。不是为了背概念,而是为了知道「数据到底是怎么从诊断仪跑到ECU里去的」。今天我们就来聊聊这个话题。

2.1 TCP/IP协议栈回顾

先看这张经典的协议栈分层图。DoIP工作在应用层,但它依赖下面四层的协同工作。

层级典型协议在DoIP中的角色
应用层DoIP、UDS封装诊断请求/响应
传输层TCP、UDP提供可靠或不可靠传输
网络层IP路由寻址,决定数据包去哪
链路层以太网物理帧封装,MAC地址通信

嗯,这里要注意:DoIP并不是直接操作网卡的。它调用的是Socket API。操作系统内核帮你完成了TCP分段、IP路由、以太网封装这些脏活累活。我们写代码时,只需要关心应用层的报文格式和Socket选项。

核心观点: DoIP开发者不需要重写TCP/IP协议栈,但必须理解每一层对诊断数据的影响。比如TCP的Nagle算法可能导致诊断响应延迟,这就是我在项目中踩过的坑。

2.2 UDP与TCP在DoIP中的应用场景

为什么DoIP同时需要UDP和TCP?说白了,两种场景需求不同。

2.2.1 UDP:用于发现与广播

UDP是无连接的,发出去就不管了。在DoIP里,它主要干两件事:
- 车辆发现:诊断仪发送广播,问「谁在?」,ECU用UDP回复「我在,这是我的IP」。
- 车辆信息通告:ECU启动时主动广播自己的DoIP能力。

我记得第一次调试车辆发现功能时,UDP广播包死活收不到。查了半天,发现是Windows防火墙把广播端口13400给拦了。嗯,这种问题在开发板上很少见,但在PC端做模拟测试时特别容易中招。

避坑指南: 我曾经在项目里用UDP发送超过1472字节的车辆信息报文,结果被IP层分片了。接收端重组失败,直接丢包。后来我强制把UDP载荷控制在1400字节以内,问题解决。记住:UDP不保证可靠,大包慎用。

2.2.2 TCP:用于诊断数据交换

诊断请求和响应必须可靠。TCP提供了确认重传、流量控制、保序机制。DoIP规定:
- 每个ECU最多支持2个TCP连接。
- 诊断数据必须通过TCP发送。
- 连接建立后,先做DoIP路由激活,才能发UDS报文。

你想想看,如果诊断数据用UDP传,万一丢了一个字节,ECU可能解析出错误的诊断指令,后果很严重。所以ISO 13400标准强制要求诊断数据走TCP。

2.3 以太网帧结构

虽然我们写代码时很少直接操作以太网帧头,但理解它有助于排查底层问题。一个标准的以太网帧长这样:

| 前导码(7B) | 定界符(1B) | 目的MAC(6B) | 源MAC(6B) | 类型(2B) | 数据(46-1500B) | FCS(4B) |

DoIP数据包就藏在「数据」字段里。类型字段如果是0x0800,表示上层是IP协议。如果是0x0806,那是ARP。

我在调试一个百兆以太网的DoIP项目时,发现诊断响应偶尔超时。用抓包工具一看,好家伙,以太网帧的FCS校验错误率高达3%。原来是PCB布线时,差分信号线没等长,导致信号质量差。嗯,硬件问题有时候比软件问题更难查。

注意: DoIP报文最大长度受限于以太网MTU(通常1500字节)。减去IP头(20B)和TCP头(20B),留给DoIP的有效载荷最多1460字节。如果你的诊断响应超过这个值,必须分片传输。ISO 13400定义了DoIP的分片机制,我们后面会详细讲。

2.4 IP地址与端口分配

DoIP使用固定的端口号,这是ISO 13400规定的,不能随便改。

端口协议用途
13400UDP车辆发现、车辆信息通告
13400TCP诊断数据交换、路由激活

IP地址方面,DoIP支持两种模式:
- 静态IP:适用于生产线或实验室环境,手动配置。
- 动态IP(DHCP):适用于售后诊断,ECU启动时自动获取IP。

我个人习惯在开发阶段用静态IP,比如192.168.0.100,避免DHCP服务器没搭好导致连不上。等协议栈稳定了,再切到DHCP模式。

还有一个容易忽略的点:DoIP要求ECU必须支持IPv4链路本地地址(169.254.x.x)。这是在没有DHCP服务器时的保底方案。我曾经在客户现场遇到过,整车网络没配DHCP,ECU拿不到IP,诊断仪连不上。后来加上链路本地地址支持,问题解决。

总结一下: 网络架构基础是DoIP的根基。UDP负责发现,TCP负责传输,以太网帧是载体,IP端口是门牌号。下一章我们开始真正动手——写DoIP的Socket初始化代码。到时候你会看到,这些基础概念是如何变成一行行C代码的。