十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

车载以太网三大协议栈实战:SOME/IP服务发现、AVB/TSN时间同步与DoIP诊断链路

车载以太网三大协议栈实战:SOME/IP服务发现、AVB/TSN时间同步与DoIP诊断链路 简介车载以太网上层应用协议详解文档面向汽车电子工程师、嵌入式开发者与车载网络技术人员系统梳理SOME/IP、AVB/TSN与DoIP三大协议的核心原理与落地实现。内容涵盖SOME/IP的按需传输、订阅/事件/字段通知、RPC调用与服务发现机制AVB/TSN的音视频流传输、Talker/Listener/Bridge组件与PTP时间同步以及DoIP在ECU刷写和诊断中的UDP/TCP应用与诊断网关协同。文档为1个docx文件压缩包大小527KB适合已有总线基础、希望深入理解车载以太网上层应用的读者作为技术参考。目前已有304人学习下载。文中不仅给出协议结构还结合实例解释实际应用帮助读者把握面向服务通信、低延迟音视频传输和IP诊断的技术要点是一份实用的车载以太网学习笔记。1. 从诊断线到以太网车载上层协议在解决什么问题如果你拆过新一代域控制器的原理图会发现一个和传统 ECU 明显不同的地方CAN 收发器旁边多了一颗以太网 PHY而且往往不止一颗。带宽从 CAN 的 500 kbps 跳到 100 Mbps 乃至 1 Gbps但你真正要解决的不是数据能不能传过去而是多个控制器在同一根总线上谁的服务可以调用、音视频流怎么按时到达、诊断仪怎么从车外访问车内节点这三件事。SOME/IP、AVB/TSN 和 DoIP 这三套协议恰好分别对应这三类诉求。它们都属于上层应用协议但工作在不同层次SOME/IP 是面向服务的中间件AVB/TSN 是保证时序和带宽的传输增强机制DoIP 则是用 IP 网络承载诊断业务的隧道。这篇文章会从实现角度拆开这三个协议栈给出最小可跑的代码、关键参数和实测中容易踩的坑适合正在做车载以太网测试、协议栈集成或者诊断开发的人往下看。2. SOME/IP 协议栈剖析服务发现、通信模式与最小实现2.1 SOME/IP 为什么被选中面向服务而不是面向信号传统 CAN 上信号是静态定义的dbc 文件里写成什么运行时就是什么。ECU 上电后周期发送报文接收方根据 ID 过滤。这种方式在软件功能持续 OTA 更新的年代变得很僵硬——你加一个功能可能要改报文矩阵甚至要动网关的路由表。SOME/IPScalable service-Oriented MiddlewarE over IP把思路从发信号改成调服务。一个 ECU 提供服务另一个 ECU 消费服务服务之间通过服务 ID、实例 ID、Method ID 来寻址。消费者在启动时用 Service DiscoverySD去找服务找到之后可以调用远程方法、订阅事件通知整个过程是动态的不再依赖静态矩阵。这个模型也有成本。既然服务发现和调用都是运行时的出问题的时候不再是你没收到报文那么简单而是服务不可用、接口版本不匹配、订阅超时这类网络语义的错误。后面写代码的时候你会发现异常处理才是真正花时间的地方。2.2 SOME/IP 报文结构从头部字段到序列化方式SOME/IP 报文头和有效载荷的结构是协议栈实现的骨架。你手里拿到一个报文第一件事就是看前 16 个字节。字段长度说明Message ID4 字节高 16 位是 Service ID低 16 位里高 8 位是 Method ID低 8 位是事件/方法标识Length4 字节从 Request ID 开始到报文结束的总长度含请求 ID单位是字节Request ID4 字节高 16 位是 Client ID低 16 位是 Session ID用于匹配请求和响应Protocol Version1 字节固定为 0x01Interface Version1 字节服务接口版本号用于兼容性校验Message Type1 字节请求(0x00)、响应(0x80)、通知(0x02)、错误(0x81)Return Code1 字节成功为 0x00错误码按接口定义Payload可变序列化后的参数数据序列化方式是贴近线缆的格式规则简单但容易写错数据按 8 字节对齐整数使用大端字节序字符串以长度前缀开头的 UTF-8 编码。如果你要做网关转发必须注意对齐填充字节不能随便丢弃否则接收端按对齐偏移去读字段会错位。2.3 服务发现SD的工作机制Offer、Find、Subscribe 三驾马车SD 使用独立的 UDP 多播地址 224.244.224.245端口 30490。所有的服务发现报文都走这个通道配置阶段就要把多播路由打通不然节点之间互相看不到。常见做法是三种报文配合工作OfferService服务端宣告我这个服务在这个 IP 上这个端口提供服务入口。这条报文让消费者不需要预先知道服务端口启动后靠监听 OfferService 就拿到了服务地址。FindService消费者主动喊话谁提供这个服务通常用于消费者启动时刻或者服务端冷启动慢于消费者的场景为了避免消费者一直空转FindService 可以周期性重发。SubscribeEventgroup消费者订阅某个事件组订阅成功之后服务端才会开始推送事件报文。这三个报文各自带一个 TTL 字段单位是秒要求接收方在 TTL/2 时间内至少收到一次重复报文否则把服务标记为不可用。这个 TTL 值就是你在配置 stage 里经常调的参数。如果设得太小网络抖动会造成服务时断时续设得太大服务真的挂了你要等很久才能发现。2.4 手写一个 SOME/IP 服务端的最小代码Python 实现下面给出一个不需要第三方库的最小 SD 事件发布实现。核心思路是自己拼报文让你能看清每个字节的作用而不是被框架隐藏掉细节。import socket import struct import time # SOME/IP 消息类型常量 SOMEIP_MSG_TYPE_NOTIFICATION 0x02 SOMEIP_MSG_TYPE_OFFER_SERVICE 0x00 # SD 中 Message Type 为 0x00 SERVICE_ID 0x1234 INSTANCE_ID 0x5678 METHOD_ID_EVENT 0x0001 CLIENT_ID 0x0000 SESSION_ID 0x0001 IFACE_VERSION 0x01 def build_someip_header(msg_type, session_id, return_code, payload_len): msg_id (SERVICE_ID 16) | (METHOD_ID_EVENT 0xFFFF) req_id (CLIENT_ID 16) | (session_id 0xFFFF) # Length 从 Request ID 开始算4(ReqID) 1(PV) 1(IV) 1(MT) 1(RC) payload total_len 8 payload_len return struct.pack( IIBBBB, msg_id, total_len, req_id, 0x01, # Protocol Version IFACE_VERSION, # Interface Version msg_type ) struct.pack(B, return_code) def build_sd_entry(ttl5): # SD entry: 这里简化为单个 OfferService entry16 字节固定 entry_type 0x00 # OfferService index 0x00 num_opt 0x00 service_id SERVICE_ID instance_id INSTANCE_ID major_version IFACE_VERSION ttl_value ttl entry struct.pack( BBBBHHB, entry_type, index, num_opt, 0x00, # 保留 service_id, instance_id, major_version ) struct.pack(I, ttl_value) return entry def main(): # 多播发送或者单播发送。为简化这里用单播推送到 192.168.1.2 send_sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) send_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) send_sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2) # 构造并发送一次 OfferService sd_payload build_sd_entry(ttl5) sd_msg build_someip_header(SOMEIP_MSG_TYPE_OFFER_SERVICE, 0x00, 0x00, len(sd_payload)) sd_payload send_sock.sendto(sd_msg, (192.168.1.2, 30490)) print(OfferService 已发送等待消费者订阅...) # 模拟服务运行持续发布事件 for i in range(10): # 事件 payload 3 个 float 值用大端序编码 payload struct.pack(fff, 1.0 * i, 2.0 * i, 3.0 * i) evt_msg build_someip_header(SOMEIP_MSG_TYPE_NOTIFICATION, 0x0001, 0x00, len(payload)) payload send_sock.sendto(evt_msg, (192.168.1.2, 30500)) print(f事件推送 {i}: payload 长度 {len(payload)}) time.sleep(1) if __name__ __main__: main()这段代码做了两个核心动作构造 SD 的 OfferService 条目并用普通 SOME/IP 报文封装然后循环推送事件报文。注意这里的SOMEIP_MSG_TYPE_OFFER_SERVICE在真实规范里是专门用于 SD 的它与普通 SOME/IP 消息的区别在于 Message ID 规定为0xFFFF8100我这里按简化处理。真实实现你需要将 Message ID 替换为0xFFFF8100Payload 条目里才放服务信息。build_someip_header里的 Length 算的是从 Request ID 起的字节数这是最容易错的地方——它不含 Message ID 和 Length 字段本身新手照着抓包器去对长度经常对不上。多播场景下IP_MULTICAST_TTL设成 2 是为了保证跨网桥时丢包率可控同一交换机直连其实设 1 就够。2.4.1 SOME/IP 参数配置里最容易出问题的三个地方TTL 和重复周期OfferService 报文不是发一次就完需要在 TTL/2 时间内重发一次。比如 TTL5那这个报文至少每 2.5 秒要重发一次。如果你只发一次消费者会过 5 秒就标记服务离线。实测中这个值在 OEM 规范里往往有固定要求不要贪心设大。UDP 还是 TCP事件通知和大部分请求-响应走 UDP但大型传输和需要可靠调用的场景要切 TCP。同一个服务可以同时暴露 UDP 和 TCP 两个入口SD 报文里用 Endpoint Option 区分传输层协议。很多问题出在消费者只订阅了 UDP服务端却发 TCP 通知。对齐填充payload 里的数据项按 8 字节对齐字符串和数组还要额外带长度字段。数据不对齐时发送端要补 0x00这些填充字节如果在网关被剪掉接收端解析会错位。最好的调试方式是抓包后手工数一遍偏移量别只信打印日志。3. AVB/TSN 实现机制时间同步、流量整形与带宽预留3.1 时间同步gPTP为什么是 AVB 的起点AVBAudio Video Bridging和 TSNTime-Sensitive Networking不是某个单一协议而是一组 IEEE 802.1 标准族。其中最先要用起来的是 802.1AS也就是 gPTPgeneralized Precision Time Protocol。音视频流要在一个交换机上做流量调度如果各个节点的本地时钟都不准就没办法知道哪个报文该在哪个时间窗口送出去。gPTP 的核心是让每个桥接节点和终端节点的时钟对齐到一个主时钟上同步精度通常在亚微秒级别。gPTP 的机制和 802.1AS 草案里的 PTP 类似主时钟节点周期性发送 Sync 报文从节点用 Follow_Up 报文里的精确发送时间戳做偏移校正再用 Peer Delay 机制测量链路延迟。这里的关键点是你不能在应用层软件里转发这些报文——时间戳必须由网络接口硬件打点所以你需要一款支持 802.1AS 的 PHY 或者 MAC 控制器。我见过很多团队在普通千兆网卡上配 Linux PTP 项目结果同步精度只能在几十微秒原因就是软件打时间戳的抖动太大。3.2 流量整形Credit-Based Shaper 和 802.1Qbv 时间感知队列AVB 最初的流量整形靠 802.1Qav也就是 Credit-Based ShaperCBS。每个 AVB 流量类有一个 credit 值有数据发送时 credit 按 idleSlope 速率增加发送时按 sendSlope 速率减少credit 低于 0 就不能发送。你配置交换机时只需要关注两个参数idleSlope 是带宽预留比例sendSlope 是发送速率。多路音视频流共享带宽时CBS 能保证每条流的突发不会影响其他流。到了 TSN 时代802.1Qbv 加入了时间感知队列通过门控列表Gate Control ListGCL来控制每个队列在特定时间段打开或关闭。比如你在 10ms 的周期内前面 2ms 只打开高优先级队列让实时控制报文独占链路后面 8ms 放开其他队列走背景流量。配置 Qbv 的关键是你要把整个网络的报文转发路径做成一个同步调度表每个交换机的门控时刻都要对齐。一个常见的坑门控周期设置过短队列里的报文还没发完就关掉了产生转发延迟设置太长低优先级流量得不到发送窗口。最优解是统计每周期内各优先级报文的字节数然后用 1500 字节帧长为单位倒推门控时间。3.3 带宽预留与流注册Stream ReservationAVB/TSN 的带宽预留走的是 802.1Qat 协议也就是 Stream Reservation ProtocolSRP。发送端用 Talker 广告要发送的流信息目的 MAC、VLAN ID、带宽需求、最大帧大小。接收端成为 Listener 后交换机沿路径做资源检查并预留带宽这就是 MSRPMultiple Stream Reservation Protocol做的事情。预留带宽时有两个参数要填对一个是基于帧间隔的frameInterval单位是 125 微秒的倍数另一个是帧大小MaxFrameSize。带宽计算不能只看平均码率要看突发窗口一个 1080p 视频流如果 GOP 大I 帧和 B 帧大小差异巨大CBS 的 idleSlope 必须按 I 帧大小来预留否则在 GOP 周期内会持续丢包。实际预留时建议在计算值上加 20% 的余量。3.4 gPTP 在 Linux 下的配置示例ptp4l 流量分类验证如果你要用 Linux 设备做 AVB 端点最稳妥的方式是配合ptp4l做 gPTP 同步再用ethtool配合 TC 做流量分类。下面给出一个可跑通的配置流程# 1. 加载 gPTP 配置文件指定使用 802.1AS 模式 cat /etc/ptp4l-gptp.conf EOF [global] domainNumber 247 logSyncInterval -3 logAnnounceInterval 1 logSyncIntervalMismatch 0 ptp_dst_mac 01:80:C2:00:00:0E network_transport L2 delay_mechanism P2P slaveOnly 0 EOF # 2. 启动 ptp4l绑定在 enp3s0 网卡上 ptp4l -f /etc/ptp4l-gptp.conf -i enp3s0 # 3. 用 pmc 工具查看主时钟状态 pmc -b 0 -u -f /etc/ptp4l-gptp.conf GET CURRENT_DATA_SET # 4. 对 AVB 流量打 802.1P 优先级 3走队列 2 tc qdisc replace dev enp3s0 parent root handle 100: mqprio \ num_tc 4 map 0 1 2 3 2 2 3 3 0 0 0 0 0 0 0 0 \ queues 10 11 12 13 \ hw 0这里的配置有几个关键点domainNumber 247是 802.1AS 约定好的 gPTP 域普通 PTP 域号是 0两个混跑会互不识别ptp_dst_mac是 gPTP 专用的目的 MAC 地址和 PTP 的01:1B:19:00:00:00不一样delay_mechanism P2P表示使用 Peer Delay 而不是 End-to-End。tc 配置里map数组的含义是 socket 优先级到队列的映射2表示 AVB 流量映射到队列 2num_tc 4划分了 4 个队列但实际映射到硬件队列的数量要看你显卡驱动是否支持多个优先队列。hw 0表示让 TC 在软件层做分类如果改hw 1要求网卡驱动支持硬件流量分发很多网卡不支持也不报错只是静默失效。配置完成后验证是否生效可以同时开一个 iperf 灌背景流量再用 ping 测量 gPTP 同步下的传输抖动# 同步精度直接看 ptp4l 日志中的 offset 值 grep offset /var/log/ptp4l.log | tail -50 # 用网络工具看 AVB 流的端到端延迟是否有周期性跳变 ping -i 0.01 -s 100 -c 100 192.168.1.2 | grep rtt如果 ptp4l 日志里的 offset 一直在几百纳秒到几微秒之间波动说明时间同步链路正常如果出现 port state change: master to passive 这类日志通常是网络里出现了两个主时钟源的冲突你需要检查交换机的 gPTP 配置。4. DoIP 诊断链路搭建ISO 13400 的报文流、端口交互与路由激活4.1 DoIP 的适用范围和测试设备连接方式DoIPDiagnostic over Internet Protocol在 ISO 13400 里定义核心目的是让外部诊断设备通过 IP 网络访问车内的 ECU。传统诊断走 CAN 或者 DoCAN诊断仪必须用 CAN 线缆进入车内 OBD 口。DoIP 的形态是OBD 口上其实是一个以太网 RJ45 或者通过 OBD 引脚引出差分对诊断仪通过 DHCP 或者 AutoIP 获取 IP 地址后与车辆端的 DoIP 实体建立 TCP 连接再走 UDS 诊断。做 DoIP 测试时最常见的接入方式有两种第一种是把 DoIP 测试仪直接连到车辆以太网交换机主要验证 Service Activation 报文交互是否完整第二种是通过 DoIP 工具链比如 CANoe 的 DoIP 选项做报文分析重点抓 TCP 层面的连接状态和 DoIP 头部字段。对 OEM 来说DoIP 的价值还体现在刷写上——DoIP 能跑 10 Mbps 以上而 CAN 只有大约 250 kbps刷一个 64MB 的升级包速度差距是数量级的。4.2 DoIP 报文结构头部字段与 Payload 类型表DoIP 报文头部固定是 8 字节后面跟着不同类型的 Payload。抓包时如果只认识 TCP 层不解析头部你很难看到诊断请求到哪里了。字段名长度说明Protocol Version1 字节0x02 表示 ISO 13400-2:2012Inverse Protocol Version1 字节0xFD与 Protocol Version 按位取反用于校验头合法性Payload Type2 字节0x0001 车辆识别请求0x0002 车辆识别响应0x0004 路由激活请求0x0005 路由激活响应0x8001 诊断报文Payload Length4 字节后面的 Payload 字节数不含这 8 字节头诊断报文Payload Type 0x8001内部再带一个 4 字节的 DoIP 诊断消息子头部其结构是源地址2 字节、目标地址2 字节然后是真正的 UDS 诊断报文。UDS 诊断报文里的 SID 都在 DoIP 的子头部之后抓包的时候要记得先剥掉 8 字节头再找 UDS 的 SID。4.3 从路由激活到 UDS 请求DoIP 完整交互时序一个标准的 DoIP 测试流程是设备接入车辆网络 → 获取 IPDHCP 或 AutoIP→ 发送车辆识别请求确认链路 → 发送路由激活请求打开诊断通道 → 发送 UDS 诊断报文。下面用 Python 模拟了关键的路由激活和 UDS 请求阶段import socket import struct # DoIP 头部 PAYLOAD_TYPE_ROUTINE_ACTIVATION 0x0005 PAYLOAD_TYPE_DIAG_MESSAGE 0x8001 def build_doip_header(payload_type, payload_len): return struct.pack(BBHL, 0x02, 0xFD, payload_type, payload_len) def route_activation_payload(): # 参数源地址 0x0E00外部测试仪激活类型 0x10默认VIN/逻辑地址预留 return struct.pack(HBB, 0x0E00, 0x10, 0x00) def diag_request_payload(source_addr, target_addr, uds_request): # 4字节源/目标地址 UDS 诊断数据 return struct.pack(HH, source_addr, target_addr) uds_request def doip_session(ecu_ip, ecu_port13400): s socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(2.0) s.connect((ecu_ip, ecu_port)) # 发送路由激活请求 act_payload route_activation_payload() s.sendall(build_doip_header(PAYLOAD_TYPE_ROUTINE_ACTIVATION, len(act_payload)) act_payload) resp s.recv(1024) print(路由激活响应 -, resp.hex()) # 发送 UDS 诊断请求: 0x22 F1 90 读取 VIN uds_req bytes.fromhex(22 F1 90) diag_payload diag_request_payload(0x0E00, 0x0001, uds_req) s.sendall(build_doip_header(PAYLOAD_TYPE_DIAG_MESSAGE, len(diag_payload)) diag_payload) resp s.recv(1024) # 剥掉8字节DoIP头后前4字节是源/目标地址之后才是 UDS 响应 vin_data resp[12:] print(UDS 响应 -, vin_data.hex()) s.close() if __name__ __main__: # 默认连接到网络上的 DoIP 边缘节点实际 IP 以 DHCP 拿到为准 doip_session(192.168.1.10)这段代码展示了两个细节。其一路由激活的源地址字段很重要它是外部诊断仪给自己分配的逻辑地址在后续的 UDS 诊断报文里源地址必须与路由激活时一致否则车辆端按 ISO 13400 规范要求直接断开 TCP 连接。其二UDS 请求放在诊断报文的子头部之后resp[12:]取的是 8 字节 DoIP 头 4 字节子头部之后的数据你要确认收到的响应字节数足够长再切做产品测试时最好先校验 Payload Length 字段再解析。4.3.1 DoIP 测试里三个容易被忽略的参数端口固定为 13400但有些 OEM 会改端口。你不能假设所有车辆的 DoIP 端口都是默认值规范允许配置为其他端口。车辆识别请求的 Payload 类型是 0x0001应答是 0x0002。测试设备如果连了多台车靠 VIN 和逻辑地址区分识别报文里 EID 和 VIN 的长度固定为 17 字节不足要用空格填充。路由激活的认证逻辑ISO 13400 里有可选的认证层级。车辆端如果配置了安全认证会返回 NACK 码测试时必须先完成认证步骤才能继续发诊断报文。5. 三合一联调自动化验证如何用一条命令验证整条链路5.1 从网络验证到应用层验证的完整检查清单把 SOME/IP、AVB/TSN 和 DoIP 放在同一个项目里做联调时故障域往往纠缠在一起。SOME/IP 的服务发现超时可能是 gPTP 不同步导致服务报文被 TSN 交换机的门控拦在队列外也可能是 DoIP 的路由激活没有真正完成导致网关不转发应用层报文。下面是一个我常用的验证脚本用 Python 把三层测试合成一个检查流程。import subprocess import socket import sys def check_gptp(ifaceenp3s0): 检查 ptp4l 是否在运行以及 offset 是否在 1us 内 try: result subprocess.run( [ptp4l, -f, /etc/ptp4l-gptp.conf, -i, iface, -s], capture_outputTrue, timeout3 ) # 实际使用时更合适的做法是解析 ptp4l 的日志或者用 phc2sys 去读时钟偏移 lines result.stdout.decode().split(\\n) offsets [float(l.split(offset)[1].split()[0]) for l in lines if offset in l] if offsets and max(abs(o) for o in offsets) 1000: print(gPTP 同步正常最大偏移 , max(offsets), ns) else: print(gPTP 同步异常或未启动) except Exception as e: print(gPTP 检查失败:, e) def check_doip_route(ecu_ip, ecu_port13400): 检查 DoIP 路由激活是否通过只发激活不发诊断报文 try: s socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(1.0) s.connect((ecu_ip, ecu_port)) # 发送 0x0005 路由激活请求 payload struct.pack(HBB, 0x0E00, 0x10, 0x00) header struct.pack(BBHL, 0x02, 0xFD, 0x0005, len(payload)) s.sendall(header payload) resp s.recv(32) if resp[4:6] b\\x00\\x06: print(DoIP 路由激活成功响应类型 0x0006) else: print(DoIP 激活失败原始响应:, resp.hex()) s.close() except Exception as e: print(DoIP 连接失败:, e) if __name__ __main__: check_gptp() check_doip_route(192.168.1.10)这段脚本覆盖了验证的先后顺序先确认时间同步正常否则后面所有基于延迟测量的结论都不可信再确认 DoIP 诊断通道没断之后做 SOME/IP 应用层测试才有意义。逻辑的顺序是刻意设计过的因为在这套网络里 gPTP 失步往往导致后续流量整形异常DoIP 通道失败通常意味着网关没有建立和诊断仪的信任关系两件事都没问题还连不上 SOME/IP才轮到去查 Service Discovery 的报文。5.2 一个高阶技巧用 TSN 门控做 DoIP 流量干扰测试DoIP 诊断刷写在整车现场最容易遇到的问题是刷写过程中正好赶上背景流量高峰TCP 分片会穿插到音视频流里然后被 TSN 交换机的流量整形机制延迟或丢弃。你可以用 Linux 的 tc 在测试环境主动构造这种干扰提前验证刷写器在弱网环境下的重传表现。# 构造一个周期性门控2ms 只开队列 3TSN 实时队列8ms 全开 tc qdisc add dev enp3s0 parent root handle 1: taprio \ num_tc 4 \ map 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 \ queues 10 11 12 13 \ base-time 0 \ sched-entry S 0x08 2000000 \ sched-entry S 0x0F 8000000 \ flags 0x2 # 让 DoIP 走队列 1与实时流量隔离 tc filter add dev enp3s0 parent 1: protocol ip prio 1 u32 \ match ip sport 13400 0xffff \ flowid 1:2门控时间配置0x08表示只打开队列 30x0F表示打开全部队列单位是纳秒所以 2ms 和 8ms 加起来正好是 10ms 的一个调度周期。DoIP 被分到队列 2 后它只在 8ms 全开窗口里发送这就模拟了刷写时打断音视频流的真实场景。多网卡多队列的硬件上你还需要确认队列编号和硬件通道的对应关系否则流量走了软件队列TAPRIO 的时间槽控制不会生效。最后用 Wireshark 过滤 TCP 的重传率重传超过 5% 就说明刷写定时器参数得调小重发间隔或者把重试窗口拉长。本文还有配套的精品资源点击获取
返回列表