QSocketNotifier的用法

发布时间:2026/10/11 5:46:58
QSocketNotifier的用法 一、类的基本信息// 头文件 #include QSocketNotifier // 类声明简化 class QSocketNotifier : public QObject { Q_OBJECT public: enum Type { Read, // 可读socket 有数据到达 Write, // 可写发送缓冲区有空位 Exception // 异常带外数据 }; QSocketNotifier(qintptr socket, Type type, QObject *parent nullptr); ~QSocketNotifier(); qintptr socket() const; Type type() const; bool isEnabled() const; void setEnabled(bool enable); // 启用/禁用 signals: void activated(QSocketDescriptor socket, QSocketNotifier::Type type); // Qt 5.15 之前: void activated(int socket); protected: bool event(QEvent *e) override; // 内部处理事件 };二、构造三个参数QSocketNotifier(qintptr socket, Type type, QObject *parent nullptr);参数 1socket类型qintptr就是平台上的 socket 句柄类型传入已经创建好的 fd/handle不是让你传一个新建的 socket而是已有的 socketint sock socket(AF_INET, SOCK_DGRAM, 0); bind(sock, ...); // 把已有的 sock 交给 notifier QSocketNotifier *n new QSocketNotifier(sock, QSocketNotifier::Read, this);参数 2typeType触发条件典型用途Readfd 可读有数据到达 / 收到连接 / 可 accept接收数据Writefd 可写发送缓冲区有空位发送数据不阻塞Exceptionfd 有异常TCP 带外数据极少用同一个 socket 可以创建多个 notifier 吗可以但每种 type 只能一个// ✅ 合法一个 Read 一个 Write auto *nr new QSocketNotifier(sock, QSocketNotifier::Read, this); auto *nw new QSocketNotifier(sock, QSocketNotifier::Write, this); // ❌ 不合法两个 Read 监听同一个 fd auto *nr1 new QSocketNotifier(sock, QSocketNotifier::Read, this); auto *nr2 new QSocketNotifier(sock, QSocketNotifier::Read, this); // 行为未定义参数 3parent标准的QObjectparent用于生命周期管理。通常传this拥有它的对象。注意QSocketNotifier不拥有socket。它析构时不会关闭 fd只是停止监听。所以 socket 的关闭由你自己负责。三、信号activatedvoid activated(QSocketDescriptor socket, QSocketNotifier::Type type);参数socket哪个 fd 触发了参数type触发类型Read/Write/ExceptionQt 版本差异Qt 5.15 前void activated(int socket)Qt 5.15void activated(QSocketDescriptor socket, QSocketNotifier::Type type)QSocketDescriptor是 Qt 6 引入的类型兼容int/qintptr。连接方式用 lambda 兼容新旧connect(notifier, QSocketNotifier::activated, this, [this]() { // 此时 socket 有数据了 // 注意不关心参数直接处理 onReadable(); });完整的最小示例示例 1单个 UDP socket 接收QT core network QT - gui CONFIG c17 console CONFIG - app_bundle TARGET SocketNotifier TEMPLATE app SOURCES main.cpp#include QCoreApplication #include QUdpSocket #include QSocketNotifier #include QNetworkDatagram #include QHostAddress #include QDebug // UDP 单播接收器使用 QSocketNotifier 监听 socket 可读事件 class UdpReceiver : public QObject { Q_OBJECT public: explicit UdpReceiver(QObject *parent nullptr) : QObject(parent) { // 绑定本地地址 192.168.137.128:6000接收发往该地址的单播数据 if (!m_udpSocket.bind(QHostAddress(192.168.137.128), 6000)) { qWarning() Bind failed: m_udpSocket.errorString(); return; } // 通过 QSocketNotifier 监听底层 socket 描述符的可读事件 m_notifier new QSocketNotifier(m_udpSocket.socketDescriptor(), QSocketNotifier::Read, this); connect(m_notifier, QSocketNotifier::activated, this, UdpReceiver::onReadyRead); qDebug() Listening on 192.9.2.100:6000; } private slots: void onReadyRead(int /*socketDescriptor*/) { // 处理期间禁用 notifier避免递归触发 m_notifier-setEnabled(false); // 循环读出所有待处理数据报 while (m_udpSocket.hasPendingDatagrams()) { QNetworkDatagram datagram m_udpSocket.receiveDatagram(); if (datagram.isValid()) { processDatagram(datagram); } } m_notifier-setEnabled(true); } private: void processDatagram(const QNetworkDatagram datagram) { const QByteArray data datagram.data(); qDebug().nospace() Received data.size() bytes from datagram.senderAddress().toString() : datagram.senderPort() | hex data.toHex() | text data; } QUdpSocket m_udpSocket; QSocketNotifier *m_notifier nullptr; }; int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); UdpReceiver receiver; return app.exec(); } #include main.moc我想使用原生的socket而不是QUdpSocket#include QCoreApplication #include QSocketNotifier #include QDebug #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include unistd.h #include cstring // UDP 单播接收器原生 socket QSocketNotifier 监听可读事件 class UdpReceiver : public QObject { Q_OBJECT public: explicit UdpReceiver(QObject *parent nullptr) : QObject(parent), m_sockfd(-1) { // 创建 IPv4 UDP 套接字 m_sockfd ::socket(AF_INET, SOCK_DGRAM, 0); if (m_sockfd 0) { qWarning() socket() failed: std::strerror(errno); return; } // 绑定本地地址 192.168.137.128:6000 sockaddr_in addr{}; addr.sin_family AF_INET; addr.sin_port htons(6000); if (::inet_pton(AF_INET, 192.168.137.128, addr.sin_addr) 0) { qWarning() inet_pton() failed: std::strerror(errno); ::close(m_sockfd); m_sockfd -1; return; } if (::bind(m_sockfd, reinterpret_castsockaddr *(addr), sizeof(addr)) 0) { qWarning() bind() failed: std::strerror(errno); ::close(m_sockfd); m_sockfd -1; return; } // 用 QSocketNotifier 监听底层描述符的可读事件 m_notifier new QSocketNotifier(m_sockfd, QSocketNotifier::Read, this); connect(m_notifier, QSocketNotifier::activated, this, UdpReceiver::onReadyRead); qDebug() Listening on 192.168.137.128:6000 (fd m_sockfd ); } ~UdpReceiver() override { if (m_sockfd 0) { ::close(m_sockfd); m_sockfd -1; } } private slots: void onReadyRead(int sockfd) { // 处理期间禁用 notifier避免递归触发 m_notifier-setEnabled(false); // 循环读出所有待处理数据报非阻塞模式下排空接收缓冲区 while (true) { char buf[65536] {}; sockaddr_in peer{}; socklen_t peerLen sizeof(peer); ssize_t n ::recvfrom(sockfd, buf, sizeof(buf), MSG_DONTWAIT, reinterpret_castsockaddr *(peer), peerLen); if (n 0) { // EAGAIN/EWOULDBLOCK 表示缓冲区已排空 if (errno ! EAGAIN errno ! EWOULDBLOCK) { qWarning() recvfrom() failed: std::strerror(errno); } break; } processDatagram(buf, size_t(n), peer); } m_notifier-setEnabled(true); } private: void processDatagram(const char *data, size_t len, const sockaddr_in peer) { char ip[INET_ADDRSTRLEN] {}; ::inet_ntop(AF_INET, peer.sin_addr, ip, sizeof(ip)); quint16 port ntohs(peer.sin_port); QByteArray hex QByteArray::fromRawData(data, int(len)).toHex(); qDebug().nospace() Received len bytes from ip : port | hex hex | text QByteArray::fromRawData(data, int(len)); } int m_sockfd; QSocketNotifier *m_notifier nullptr; }; int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); UdpReceiver receiver; return app.exec(); } #include main.moc再帮我把.h和.cpp分离处理我不想要放在一个文件里面#ifndef UDPRECEIVER_H #define UDPRECEIVER_H #include QObject #include QSocketNotifier #include netinet/in.h // UDP 单播接收器原生 socket QSocketNotifier 监听可读事件 class UdpReceiver : public QObject { Q_OBJECT public: explicit UdpReceiver(QObject *parent nullptr); ~UdpReceiver() override; private slots: void onReadyRead(int sockfd); private: void processDatagram(const char *data, size_t len, const sockaddr_in peer); int m_sockfd; QSocketNotifier *m_notifier nullptr; }; #endif // UDPRECEIVER_H#include UdpReceiver.h #include QDebug #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include unistd.h #include cstring UdpReceiver::UdpReceiver(QObject *parent) : QObject(parent), m_sockfd(-1) { // 创建 IPv4 UDP 套接字 m_sockfd ::socket(AF_INET, SOCK_DGRAM, 0); if (m_sockfd 0) { qWarning() socket() failed: std::strerror(errno); return; } // 绑定本地地址 192.168.137.128:6000 sockaddr_in addr{}; addr.sin_family AF_INET; addr.sin_port htons(6000); if (::inet_pton(AF_INET, 192.168.137.128, addr.sin_addr) 0) { qWarning() inet_pton() failed: std::strerror(errno); ::close(m_sockfd); m_sockfd -1; return; } if (::bind(m_sockfd, reinterpret_castsockaddr *(addr), sizeof(addr)) 0) { qWarning() bind() failed: std::strerror(errno); ::close(m_sockfd); m_sockfd -1; return; } // 用 QSocketNotifier 监听底层描述符的可读事件 m_notifier new QSocketNotifier(m_sockfd, QSocketNotifier::Read, this); connect(m_notifier, QSocketNotifier::activated, this, UdpReceiver::onReadyRead); qDebug() Listening on 192.168.137.128:6000 (fd m_sockfd ); } UdpReceiver::~UdpReceiver() { if (m_sockfd 0) { ::close(m_sockfd); m_sockfd -1; } } void UdpReceiver::onReadyRead(int sockfd) { // 处理期间禁用 notifier避免递归触发 m_notifier-setEnabled(false); // 循环读出所有待处理数据报非阻塞模式下排空接收缓冲区 while (true) { char buf[65536] {}; sockaddr_in peer{}; socklen_t peerLen sizeof(peer); ssize_t n ::recvfrom(sockfd, buf, sizeof(buf), MSG_DONTWAIT, reinterpret_castsockaddr *(peer), peerLen); if (n 0) { // EAGAIN/EWOULDBLOCK 表示缓冲区已排空 if (errno ! EAGAIN errno ! EWOULDBLOCK) { qWarning() recvfrom() failed: std::strerror(errno); } break; } processDatagram(buf, size_t(n), peer); } m_notifier-setEnabled(true); } void UdpReceiver::processDatagram(const char *data, size_t len, const sockaddr_in peer) { char ip[INET_ADDRSTRLEN] {}; ::inet_ntop(AF_INET, peer.sin_addr, ip, sizeof(ip)); quint16 port ntohs(peer.sin_port); QByteArray hex QByteArray::fromRawData(data, int(len)).toHex(); qDebug().nospace() Received len bytes from ip : port | hex hex | text QByteArray::fromRawData(data, int(len)); }#include QCoreApplication #include UdpReceiver.h int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); UdpReceiver receiver; return app.exec(); }ps: 发送端使用网络调试助手一直周期性发送信息。现在我们实现了1个socket的接收逻辑继续添加192.168.137.128:4000和192.168.137.128:5000 的接收逻辑#ifndef UDPRECEIVER_H #define UDPRECEIVER_H #include QObject #include QSocketNotifier #include QString #include netinet/in.h // UDP 单播接收器原生 socket QSocketNotifier 监听可读事件 // 一个实例负责一个本地 (ip, port) 套接字 class UdpReceiver : public QObject { Q_OBJECT public: explicit UdpReceiver(const QString ip, quint16 port, QObject *parent nullptr); ~UdpReceiver() override; private slots: void onReadyRead(int sockfd); private: void processDatagram(const char *data, size_t len, const sockaddr_in peer); int m_sockfd; quint16 m_port; QSocketNotifier *m_notifier nullptr; }; #endif // UDPRECEIVER_H#include UdpReceiver.h #include QDebug #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include unistd.h #include cstring UdpReceiver::UdpReceiver(const QString ip, quint16 port, QObject *parent) : QObject(parent), m_sockfd(-1), m_port(port) { // 创建 IPv4 UDP 套接字 m_sockfd ::socket(AF_INET, SOCK_DGRAM, 0); if (m_sockfd 0) { qWarning() socket() failed: std::strerror(errno); return; } // 绑定本地地址 ip:port sockaddr_in addr{}; addr.sin_family AF_INET; addr.sin_port htons(port); if (::inet_pton(AF_INET, ip.toUtf8().constData(), addr.sin_addr) 0) { qWarning() inet_pton() failed: std::strerror(errno); ::close(m_sockfd); m_sockfd -1; return; } if (::bind(m_sockfd, reinterpret_castsockaddr *(addr), sizeof(addr)) 0) { qWarning().nospace() bind( ip : port ) failed: std::strerror(errno); ::close(m_sockfd); m_sockfd -1; return; } // 用 QSocketNotifier 监听底层描述符的可读事件 m_notifier new QSocketNotifier(m_sockfd, QSocketNotifier::Read, this); connect(m_notifier, QSocketNotifier::activated, this, UdpReceiver::onReadyRead); qDebug().nospace() Listening on ip : port (fd m_sockfd ); } UdpReceiver::~UdpReceiver() { if (m_sockfd 0) { ::close(m_sockfd); m_sockfd -1; } } void UdpReceiver::onReadyRead(int sockfd) { // 处理期间禁用 notifier避免递归触发 m_notifier-setEnabled(false); // 循环读出所有待处理数据报非阻塞模式下排空接收缓冲区 while (true) { char buf[65536] {}; sockaddr_in peer{}; socklen_t peerLen sizeof(peer); ssize_t n ::recvfrom(sockfd, buf, sizeof(buf), MSG_DONTWAIT, reinterpret_castsockaddr *(peer), peerLen); if (n 0) { // EAGAIN/EWOULDBLOCK 表示缓冲区已排空 if (errno ! EAGAIN errno ! EWOULDBLOCK) { qWarning() recvfrom() failed: std::strerror(errno); } break; } processDatagram(buf, size_t(n), peer); } m_notifier-setEnabled(true); } void UdpReceiver::processDatagram(const char *data, size_t len, const sockaddr_in peer) { char ip[INET_ADDRSTRLEN] {}; ::inet_ntop(AF_INET, peer.sin_addr, ip, sizeof(ip)); quint16 port ntohs(peer.sin_port); QByteArray hex QByteArray::fromRawData(data, int(len)).toHex(); qDebug().nospace() [port m_port ] Received len bytes from ip : port | hex hex | text QByteArray::fromRawData(data, int(len)); }#include QCoreApplication #include UdpReceiver.h int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); const QString ip QStringLiteral(192.168.137.128); UdpReceiver r1(ip, 4000); UdpReceiver r2(ip, 5000); UdpReceiver r3(ip, 6000); return app.exec(); }新增需求现在我们简单实现一个转发逻辑如果是4000和5000收的就通过6001转发给发送方ip的6000如果是6000收的就转发给发送方ip的4000和5000端口接收bind 发送源bind192.168.137.128:4000 192.168.137.128:4001192.168.137.128:5000 192.168.137.128:5001192.168.137.128:6000 192.168.137.128:6001规则 1在 4000 或 5000 收到数据用 sender 6001配对 recv 6000发往 peer IP :6000规则 2在 6000 收到数据发往 peer IP :4000 → 用 sender 4001配对 recv 4000发往 peer IP :5000 → 用 sender 5001配对 recv 5000测试性能100ms一次10ms一次1ms一次:环境软件部署在虚拟机上的ubuntu中。调试助手在本机Windows电脑上QT core network QT - gui CONFIG c17 console CONFIG - app_bundle TARGET SocketNotifier TEMPLATE app HEADERS UdpReceiver.h \ UdpSender.h SOURCES main.cpp \ UdpReceiver.cpp \ UdpSender.cpp#ifndef UDPRECEIVER_H #define UDPRECEIVER_H #include QObject #include QSocketNotifier #include QString #include QByteArray #include netinet/in.h // UDP 单播接收器原生 socket QSocketNotifier 监听可读事件 // 一个实例负责一个本地 (ip, port) 套接字 class UdpReceiver : public QObject { Q_OBJECT public: explicit UdpReceiver(const QString ip, quint16 port, QObject *parent nullptr); ~UdpReceiver() override; signals: // 收到数据报时发出携带数据、对端IP、本机接收端口 void datagramReceived(const QByteArray data, const QString peerIp, quint16 recvPort); private slots: void onReadyRead(int sockfd); private: void processDatagram(const char *data, size_t len, const sockaddr_in peer); int m_sockfd; quint16 m_port; QSocketNotifier *m_notifier nullptr; }; #endif // UDPRECEIVER_H#include UdpReceiver.h #include QDebug #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include unistd.h #include cstring UdpReceiver::UdpReceiver(const QString ip, quint16 port, QObject *parent) : QObject(parent), m_sockfd(-1), m_port(port) { // 创建 IPv4 UDP 套接字 m_sockfd ::socket(AF_INET, SOCK_DGRAM, 0); if (m_sockfd 0) { qWarning() socket() failed: std::strerror(errno); return; } // 绑定本地地址 ip:port sockaddr_in addr{}; addr.sin_family AF_INET; addr.sin_port htons(port); if (::inet_pton(AF_INET, ip.toUtf8().constData(), addr.sin_addr) 0) { qWarning() inet_pton() failed: std::strerror(errno); ::close(m_sockfd); m_sockfd -1; return; } if (::bind(m_sockfd, reinterpret_castsockaddr *(addr), sizeof(addr)) 0) { qWarning().nospace() bind( ip : port ) failed: std::strerror(errno); ::close(m_sockfd); m_sockfd -1; return; } // 用 QSocketNotifier 监听底层描述符的可读事件 m_notifier new QSocketNotifier(m_sockfd, QSocketNotifier::Read, this); connect(m_notifier, QSocketNotifier::activated, this, UdpReceiver::onReadyRead); qDebug().nospace() Listening on ip : port (fd m_sockfd ); } UdpReceiver::~UdpReceiver() { if (m_sockfd 0) { ::close(m_sockfd); m_sockfd -1; } } void UdpReceiver::onReadyRead(int sockfd) { // 处理期间禁用 notifier避免递归触发 m_notifier-setEnabled(false); // 循环读出所有待处理数据报非阻塞模式下排空接收缓冲区 while (true) { char buf[65536] {}; sockaddr_in peer{}; socklen_t peerLen sizeof(peer); ssize_t n ::recvfrom(sockfd, buf, sizeof(buf), MSG_DONTWAIT, reinterpret_castsockaddr *(peer), peerLen); if (n 0) { // EAGAIN/EWOULDBLOCK 表示缓冲区已排空 if (errno ! EAGAIN errno ! EWOULDBLOCK) { qWarning() recvfrom() failed: std::strerror(errno); } break; } processDatagram(buf, size_t(n), peer); } m_notifier-setEnabled(true); } void UdpReceiver::processDatagram(const char *data, size_t len, const sockaddr_in peer) { char ip[INET_ADDRSTRLEN] {}; ::inet_ntop(AF_INET, peer.sin_addr, ip, sizeof(ip)); quint16 port ntohs(peer.sin_port); QByteArray hex QByteArray::fromRawData(data, int(len)).toHex(); qDebug().nospace() [port m_port ] Received len bytes from ip : port | hex hex | text QByteArray::fromRawData(data, int(len)); // 通知外部转发逻辑携带数据、对端IP、本机接收端口 emit datagramReceived(QByteArray(data, int(len)), QString::fromLatin1(ip), m_port); }#ifndef UDPSENDER_H #define UDPSENDER_H #include QObject #include QString #include QByteArray // UDP 单播发送器原生 socketbind 到固定源 (srcIp, srcPort) // 目标地址由每次 send() 指定 // 与 UdpReceiver 一一对应源端口 4001/5001/6001 对应接收 4000/5000/6000 class UdpSender : public QObject { Q_OBJECT public: explicit UdpSender(const QString srcIp, quint16 srcPort, QObject *parent nullptr); ~UdpSender() override; // 向 destIp:destPort 发送数据报返回是否完整发送成功 bool send(const QByteArray data, const QString destIp, quint16 destPort); bool send(const char *data, size_t len, const QString destIp, quint16 destPort); bool isValid() const { return m_sockfd 0; } quint16 sourcePort() const { return m_srcPort; } private: int m_sockfd; quint16 m_srcPort; }; #endif // UDPSENDER_H#include UdpSender.h #include QDebug #include sys/socket.h #include netinet/in.h #include arpa/inet.h #include unistd.h #include cstring UdpSender::UdpSender(const QString srcIp, quint16 srcPort, QObject *parent) : QObject(parent), m_sockfd(-1), m_srcPort(srcPort) { // 创建 IPv4 UDP 套接字 m_sockfd ::socket(AF_INET, SOCK_DGRAM, 0); if (m_sockfd 0) { qWarning() socket() failed: std::strerror(errno); return; } // bind 到源地址使发出的报文源端口固定为 srcPort sockaddr_in srcAddr{}; srcAddr.sin_family AF_INET; srcAddr.sin_port htons(srcPort); if (::inet_pton(AF_INET, srcIp.toUtf8().constData(), srcAddr.sin_addr) 0) { qWarning() inet_pton() failed: std::strerror(errno); ::close(m_sockfd); m_sockfd -1; return; } if (::bind(m_sockfd, reinterpret_castsockaddr *(srcAddr), sizeof(srcAddr)) 0) { qWarning().nospace() bind( srcIp : srcPort ) failed: std::strerror(errno); ::close(m_sockfd); m_sockfd -1; return; } qDebug().nospace() Sender bound on srcIp : srcPort (fd m_sockfd ); } UdpSender::~UdpSender() { if (m_sockfd 0) { ::close(m_sockfd); m_sockfd -1; } } bool UdpSender::send(const QByteArray data, const QString destIp, quint16 destPort) { return send(data.constData(), size_t(data.size()), destIp, destPort); } bool UdpSender::send(const char *data, size_t len, const QString destIp, quint16 destPort) { if (m_sockfd 0) { qWarning() send() called on invalid socket; return false; } // 每次按入参构造目标地址 sockaddr_in destAddr{}; destAddr.sin_family AF_INET; destAddr.sin_port htons(destPort); if (::inet_pton(AF_INET, destIp.toUtf8().constData(), destAddr.sin_addr) 0) { qWarning().nospace() send(): invalid dest ip destIp; return false; } ssize_t n ::sendto(m_sockfd, data, len, 0, reinterpret_castsockaddr *(destAddr), sizeof(destAddr)); if (n 0) { qWarning().nospace() sendto(srcPort m_srcPort - destIp : destPort ) failed: std::strerror(errno); return false; } return size_t(n) len; }#include QCoreApplication #include UdpReceiver.h #include UdpSender.h int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); const QString ip QStringLiteral(192.168.137.128); // 接收端4000 / 5000 / 6000 UdpReceiver r4000(ip, 4000); UdpReceiver r5000(ip, 5000); UdpReceiver r6000(ip, 6000); // 发送端源端口4001 / 5001 / 6001 UdpSender s4001(ip, 4001); UdpSender s5001(ip, 5001); UdpSender s6001(ip, 6001); // 转发规则 // recv 4000/5000 - sender 6001 - peer:6000 // recv 6000 - sender 4001 - peer:4000 且 sender 5001 - peer:5000 auto forward [s4001, s5001, s6001](const QByteArray data, const QString peerIp, quint16 recvPort) { if (recvPort 4000 || recvPort 5000) { s6001.send(data, peerIp, 6000); } else if (recvPort 6000) { s4001.send(data, peerIp, 4000); s5001.send(data, peerIp, 5000); } }; QObject::connect(r4000, UdpReceiver::datagramReceived, forward); QObject::connect(r5000, UdpReceiver::datagramReceived, forward); QObject::connect(r6000, UdpReceiver::datagramReceived, forward); return app.exec(); }进一步添加需求是这样的实际上我的转发规则比较复杂现在我准备引入我的复杂逻辑了你可以帮我先把接口给写出来吗?#ifndef IFORWARDER_H #define IFORWARDER_H #include QObject #include QByteArray #include QString // 转发器接口接收一条数据报决定转发动作 // 具体转发规则分流/丢弃/修改/多目标复制等由子类实现 class IForwarder : public QObject { Q_OBJECT public: explicit IForwarder(QObject *parent nullptr) : QObject(parent) {} ~IForwarder() override default; // 处理一条收到的数据报 // data : 原始数据 // peerIp : 发送方 IP // recvPort : 本机接收端口 virtual void process(const QByteArray data, const QString peerIp, quint16 recvPort) 0; }; #endif // IFORWARDER_H#ifndef FORWARDER4000_5000_H #define FORWARDER4000_5000_H #include IForwarder.h class UdpSender; // 转发器处理 r4000 / r5000 收到的数据报 // process() 由使用者自行实现 class Forwarder4000_5000 : public IForwarder { Q_OBJECT public: // 注入三个发送器源端口分别为 4001 / 5001 / 6001 explicit Forwarder4000_5000(UdpSender *s4001, UdpSender *s5001, UdpSender *s6001, QObject *parent nullptr); void process(const QByteArray data, const QString peerIp, quint16 recvPort) override; private: // 便捷方法用指定 sender 发往 peerIp:destPort void sendTo(UdpSender *sender, const QByteArray data, const QString peerIp, quint16 destPort); UdpSender *m_s4001 nullptr; UdpSender *m_s5001 nullptr; UdpSender *m_s6001 nullptr; }; #endif // FORWARDER4000_5000_H#include Forwarder4000_5000.h #include UdpSender.h Forwarder4000_5000::Forwarder4000_5000(UdpSender *s4001, UdpSender *s5001, UdpSender *s6001, QObject *parent) : IForwarder(parent), m_s4001(s4001), m_s5001(s5001), m_s6001(s6001) { } void Forwarder4000_5000::process(const QByteArray data, const QString peerIp, quint16 recvPort) { // TODO: 由使用者实现 r4000 / r5000 的解析与分发逻辑 // 可用成员m_s4001 / m_s5001 / m_s6001 // 可用辅助sendTo(sender, data, peerIp, destPort) Q_UNUSED(data); Q_UNUSED(peerIp); Q_UNUSED(recvPort); } void Forwarder4000_5000::sendTo(UdpSender *sender, const QByteArray data, const QString peerIp, quint16 destPort) { if (sender sender-isValid()) { sender-send(data, peerIp, destPort); } }#ifndef FORWARDER6000_H #define FORWARDER6000_H #include IForwarder.h class UdpSender; // 转发器处理 r6000 收到的数据报 // process() 由使用者自行实现 class Forwarder6000 : public IForwarder { Q_OBJECT public: // 注入三个发送器源端口分别为 4001 / 5001 / 6001 explicit Forwarder6000(UdpSender *s4001, UdpSender *s5001, UdpSender *s6001, QObject *parent nullptr); void process(const QByteArray data, const QString peerIp, quint16 recvPort) override; private: // 便捷方法用指定 sender 发往 peerIp:destPort void sendTo(UdpSender *sender, const QByteArray data, const QString peerIp, quint16 destPort); UdpSender *m_s4001 nullptr; UdpSender *m_s5001 nullptr; UdpSender *m_s6001 nullptr; }; #endif // FORWARDER6000_H#include Forwarder6000.h #include UdpSender.h Forwarder6000::Forwarder6000(UdpSender *s4001, UdpSender *s5001, UdpSender *s6001, QObject *parent) : IForwarder(parent), m_s4001(s4001), m_s5001(s5001), m_s6001(s6001) { } void Forwarder6000::process(const QByteArray data, const QString peerIp, quint16 recvPort) { // TODO: 由使用者实现 r6000 的解析与分发逻辑 // 可用成员m_s4001 / m_s5001 / m_s6001 // 可用辅助sendTo(sender, data, peerIp, destPort) Q_UNUSED(data); Q_UNUSED(peerIp); Q_UNUSED(recvPort); } void Forwarder6000::sendTo(UdpSender *sender, const QByteArray data, const QString peerIp, quint16 destPort) { if (sender sender-isValid()) { sender-send(data, peerIp, destPort); } }#include QCoreApplication #include UdpReceiver.h #include UdpSender.h #include Forwarder4000_5000.h #include Forwarder6000.h int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); const QString ip QStringLiteral(192.168.137.128); // 接收端4000 / 5000 / 6000 UdpReceiver r4000(ip, 4000); UdpReceiver r5000(ip, 5000); UdpReceiver r6000(ip, 6000); // 发送端源端口4001 / 5001 / 6001 UdpSender s4001(ip, 4001); UdpSender s5001(ip, 5001); UdpSender s6001(ip, 6001); // 转发器r4000/r5000 - Forwarder4000_5000r6000 - Forwarder6000 Forwarder4000_5000 fwdLow(s4001, s5001, s6001); Forwarder6000 fwdHigh(s4001, s5001, s6001); // 接线 QObject::connect(r4000, UdpReceiver::datagramReceived, fwdLow, IForwarder::process); QObject::connect(r5000, UdpReceiver::datagramReceived, fwdLow, IForwarder::process); QObject::connect(r6000, UdpReceiver::datagramReceived, fwdHigh, IForwarder::process); return app.exec(); }后来我又改写了下重命名了一些变量名引入了我的软件的真实逻辑并进行了实机测试发现这种架构就可以满足我的软件的需求转发的延迟在可以接收的范围内至少体感不算那么明显。所以就使用这版了。属于单线程直通反而比之前使用3个线程接收1个子线程处理快估计是因为使用了Qt的信号槽吧。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询