udp数据流转---分析_Part2

udp数据流转---分析-CSDN博客

我感觉我的软件延迟很高

我现在觉得原因是:

(1)使用3个线程来接收3个支路的信息,使用emit 发送数据

(这已经使用了Qt的队列(跨线程))

(2)在接收端又使用了3个队列来管理3个支路,然后转发并处理。(又使用了队列)

一、先把你的数据链路画出来

根据你的描述,我推断链路是这样的:

cpp 复制代码
┌─────────────┐   ┌─────────────┐   ┌─────────────┐
│ 接收线程1    │   │ 接收线程2    │   │ 接收线程3    │
│ recv(sock1) │   │ recv(sock2) │   │ recv(sock3) │
│    │        │   │    │        │   │    │        │
│  emit ①     │   │  emit ①     │   │  emit ①     │
└────┼────────┘   └────┼────────┘   └────┼────────┘
     │                 │                 │
     ▼                 ▼                 ▼
┌──────────────────────────────────────────────┐
│            Qt 事件队列(跨线程,第 1 次排队)    │
└──────────────────────────────────────────────┘
     │                 │                 │
     ▼                 ▼                 ▼
┌─────────────┐   ┌─────────────┐   ┌─────────────┐
│ 队列1        │   │ 队列2        │   │ 队列3        │
│(第 2 次排队)│   │(第 2 次排队)│   │(第 2 次排队)│
└──────┬──────┘   └──────┬──────┘   └──────┬──────┘
       │                 │                 │
       ▼                 ▼                 ▼
┌──────────────────────────────────────────────┐
│           处理线程 / 转发逻辑                   │
│      从 3 个队列取 → 处理 → sendto             │
└──────────────────────────────────────────────┘

每一段都是一个"生产者-消费者"队列,每个队列都可能积压。

二、逐段量化延迟

假设你的包速率是 1 万包/秒(每包 0.1ms 到达间隔)。

第 1 次排队:emit → Qt 事件队列

emit sign_bdp(bdp) 跨线程投递,实际上是:

cpp 复制代码
接收线程:构造 QMetaCallEvent → 加锁 → 放入目标线程事件队列 → 写 eventfd 唤醒
目标线程:事件循环被唤醒 → 取事件 → 加锁 → 调用槽函数

单次 emit 的延迟:

  • 加锁 + 入队 + 唤醒:约 5~20 微秒

  • 如果目标线程正忙:排队等待 = 队列长度 × 单次处理时间

队列积压时的延迟:

队列长度 目标线程单次处理 新包等待时间
100 10 微秒 1 毫秒
1000 10 微秒 10 毫秒
10000 10 微秒 100 毫秒
100000 10 微秒 1 秒

这就是第一层延迟。如果目标线程处理慢(比如 GUI 线程还要画界面),队列会持续增长,延迟线性上升。

第 2 次排队:从 Qt 队列取出 → 放入自己的队列

如果接收槽函数只是把数据再塞进自己的队列,那么:

cpp 复制代码
槽函数被调用 → 加锁 → 放入队列1 → 解锁 → 返回

这一段本身很快 (几微秒),但它不解决问题 ,只是把延迟转移到下一个环节。

第 3 段:处理线程从队列取 → 处理 → 转发

cpp 复制代码
处理线程:从队列1取 → 处理 → sendto

正确的做法 :减少中间环节,让数据尽快到达处理逻辑




方案 1:合并两段队列(收益最大)

去掉中间的 emit 或去掉中间的队列,只保留一次排队。

做法 A:接收线程直接处理 + 转发,不 emit

做法 B:3 个接收线程 + 1 个处理线程,但只用一次队列

cpp 复制代码
接收线程1 ─┐
接收线程2 ─┼──► 一个共享队列 ──► 处理线程
接收线程3 ─┘

接收线程收到数据后,直接放入处理队列 (不用 emit,用 QMutex + QQueue 或无锁队列),处理线程从队列取。

好处:

  • 只有一次排队

  • 接收线程只做 recvfrom + 入队,极快

  • 处理线程专注处理

代价:

  • 需要自己实现队列(QMutex + QWaitCondition 或 QQueue + std::mutex)

  • 不如 emit 优雅,但快得多




方案 2:一个线程管 3 条支路(如果速率不高)

如果 3 条总速率 < 5 万包/秒,一个线程 + QSocketNotifier 是最优:

cpp 复制代码
一个线程:
  QSocketNotifier × 3 → onReadable(idx) → 处理 → 转发

零跨线程投递,零队列,延迟最低。

前提:处理逻辑不阻塞、不碰 GUI。




结合你的场景(3 条 UDP 支路,互相转发,Qt):

推荐架构

cpp 复制代码
┌──────────────────────────────────────────────┐
│              一个工作线程                      │
│                                               │
│   QSocketNotifier × 3                        │
│     sock1 ──┐                                │
│     sock2 ──┼──► onReadable(idx)             │
│     sock3 ──┘         │                      │
│                       ▼                      │
│              处理 + 转发给另外两条             │
│                       │                      │
│                       ▼                      │
│              批量 emit(给 GUI,20ms 一次)    │
└──────────────────────────────────────────────┘

理由:

  1. 一个线程 → 零跨线程队列,零 emit 延迟

  2. QSocketNotifier → 无数据不占 CPU

  3. 处理 + 转发在同一个线程 → 共享 socket 无需加锁

  4. 只对 GUI 显示 用批量 emit ,且降频到 50 次/秒

相关推荐
福大大架构师每日一题2 小时前
mediamtx v1.21.2发布:UDP、JWT、RTSP、RTMP、HLS、WebRTC全面修复,稳定性与安全性再提升
网络协议·udp·webrtc
北京盛世宏博3 小时前
多协议以太网温湿度变送器:Modbus TCP/UDP/SNMP 协议应用场景详解
单片机·tcp/ip·udp·传感器·温湿度
15Moonlight15 小时前
Linux网络(02):Socket 编程 UDP
linux·网络·udp
盛世宏博北京2 天前
工业环境稳定采集:配电室 IP 温湿度变送器 TCP/UDP 通信部署指南
单片机·tcp/ip·udp
Multipath7123 天前
无人载具的高可靠低时延聚合组网方案
网络·网络协议·安全·udp·智能路由器
洋不写bug4 天前
网络原理(一)应用层协议、传输层UDP/TCP报文格式解析
网络·tcp/ip·udp·tcp·应用层·javaee·传输层
cyf315 天前
RDMA数据传输原理深入解读
udp·rdma·rocev2·bth·pertest
ShyanZh6 天前
【Python3基础】19-Socket 与 TCP、UDP 编程
python·网络协议·tcp/ip·udp
流浪0016 天前
计算机网络篇3:端口号与 socket:数据到达主机后,如何找到目标进程?
计算机网络·udp·tcp·端口·网络字节序