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 次/秒

相关推荐
2501_915106321 天前
抓包抓到乱码或压缩字节怎么解析,TraceEagle五种查看模式和一键解码
网络协议·计算机网络·网络安全·ios·adb·https·udp
流浪0011 天前
计算机网络篇14:传输层——UDP 协议:8 字节定长报头划出报文边界,16 位端口号完成分用
计算机网络·udp·udp包头结构
从琳开始9892 天前
从Socket到UDP回显服务器:Java网络编程完整指南
java·网络·网络协议·udp·java-ee
Lhan.zzZ2 天前
用 Qt 构建线程安全的通讯静态库:从线程亲和性到完整工程
c++·vscode·qt·tcp/ip·udp
听风说ml2 天前
socket编程 【C语言】
c语言·udp·tcp·socket编程
zl_dfq2 天前
Windows 下 UDP 套接字编程指南
网络·windows·udp
上海云盾-小余3 天前
七层 CC 攻击检测难点:如何区分真实用户与攻击请求
网络·人工智能·windows·tcp/ip·udp
Multipath7124 天前
上车即入院,链路先到院——乾元通多链路聚合,为智慧救护车守住移动中的生命线
网络·人工智能·网络协议·udp·无人机
幻智星科技4 天前
UDP桥接IP产品简介
tcp/ip·fpga开发·udp·硬件架构
hhhjhl4 天前
Protobuf.js 跨端三方库仓颉适配全流程
javascript·网络·udp