拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Qt实现UDP单播、组播和广播功能

一、UDP单播、广播和组播的说明UDP是不可靠、无连接的所以划分为发送方和接收方更好理解1、单播UDP是无连接的进行单播通信时必须要绑定接收方端口发送方直接通过接收方的ip和绑定的端口进行通信。发送方可以绑定端口也可以不用绑定端口不绑定端口的话系统会随机分配端口。对于多网卡来说需要指定网卡绑定一个网卡的ip。如果不进行显式的绑定操作QUdpSocket对象将会使用默认的绑定方式自动选择一个可用的 ip 地址进行绑定。2、广播对于只有1个网卡的主机来说可以不用显示绑定ip发送方直接发送广播就行接收方绑定广播端口就行这样才能看到收到的消息。对于多网卡来说要指定唯一的网卡ip并且在广播前要绑定广播端口。ip可以不用绑定这样系统会随机分配一个网卡ip。3、组播对于只有1个网卡的主机来说可以不用绑定ip直接绑定端口后加入组播就行。系统分配任意一个ip 相当于QHostAddress::AnyIPv4。对于多网卡来说要指定唯一的网卡并且在加入组播前要绑定组播端口ip可以不用绑定系统分配任意一个ip。注意指定网卡不等于指定ip1、使用setMulticastInterface方法可以指定一个明确的网卡但并不意味着只有一个 IP 地址。一个网卡可以绑定多个 IP 地址例如在同一台主机上同时存在有线网卡和无线网卡它们可能都连接到同一局域网并分别配置了不同的 IP 地址。此时通过setMulticastInterface方法指定了一个明确的网卡后并不确定使用哪个 IP 地址来进行组播通信。如果需要确保使用特定的 IP 地址进行组播通信则需要使用bind方法来将QUdpSocket对象绑定到具体的 IP 地址和端口上这样每次进行组播通信时都会使用该 IP 地址来发送和接收数据报文。2、使用QHostAddress::AnyIPv4参数可以将QUdpSocket对象绑定到本机的所有 IPv4 地址。这意味着该QUdpSocket对象可以接收通过本机的任意一个 IPv4 地址发送到指定端口的数据包。然而需要注意的是绑定到多个 IPv4 地址并不意味着可以同时从多个地址接收数据包。在任何给定的时刻QUdpSocket对象只能通过一个 IP 地址接收数据包。当有多个 IPv4 地址可用时QUdpSocket对象会选择其中一个地址来接收数据包。这个选择通常由操作系统或网络栈决定并且可能会受到各种因素的影响例如网络接口的优先级、路由表等。因此使用QHostAddress::AnyIPv4参数可以让QUdpSocket对象绑定到本机的所有 IPv4 地址但实际上它只能通过其中一个地址接收数据包。具体使用哪个地址取决于操作系统和网络环境。二、遇到的UDP通信的问题参考查看所有端口netstat -ano查看某个端口netstat -ano | findstr 端口号查看TCP的socket信息netstat -an | find TCP查看UDP的socket信息netstat -an | find UDP关于QT UDP组播的几个问题https://blog.csdn.net/tom06/article/details/52163665?spm1001.2014.3001.5506UDP多播/组播通信同一局域网下的两台机器通信接收不到数据https://blog.csdn.net/qq_43290013/article/details/117288296?spm1001.2014.3001.5506QT读取网卡列表多网卡绑定组播网卡https://blog.csdn.net/qq_30727593/article/details/127441711?spm1001.2014.3001.5506三、效果与代码1、在.pro文件中添加如下内容QT network2、在.h文件中添加串口所用的头文件#include QUdpSocket #include QNetworkInterface3、添加一个QUdpSocket* socket的类成员并在.cpp中实例化对象socket new QUdpSocket;4、扫描可用网口QListQNetworkInterface interfaceList QNetworkInterface::allInterfaces(); foreach (QNetworkInterface nif, interfaceList) { // 检查网卡是否有效并已经启用 if (nif.isValid() nif.flags().testFlag(QNetworkInterface::IsUp)) { // 将已经启用的网卡名称添加到列表中 enabledInterfaceList.append(nif); QListQNetworkAddressEntry entries nif.addressEntries(); foreach (QNetworkAddressEntry entry, entries) { if (entry.ip().protocol() QAbstractSocket::IPv4Protocol) { if(entry.ip().toString() localIP){ index; }; } } } }5、对组播进行设置可以忽略//组播的数据的生存期数据报没跨1个路由就会减1.表示多播数据报只能在同一路由下的局域网内传播 socket-setSocketOption(QAbstractSocket::MulticastTtlOption,1); //1是允许loopback模式自发自收,0是阻止。 socket-setSocketOption(QAbstractSocket::MulticastLoopbackOption, true);6、初始化组播设置int MainWindow::init_group(QUdpSocket *socket, QNetworkInterface currentInterface, QHostAddress localAddress, QHostAddress targetAddress, int localPort) { if (targetAddress.isMulticast()) {//isMulticast()判断是否是组播地址 if (localPort 0) {//判断是否指定本地端口 //bind(localAddress, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)可以换成bind(QHostAddress::AnyIPv4, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint) if (socket-bind(localAddress, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)) { socket-setMulticastInterface(currentInterface); if (socket-joinMulticastGroup(targetAddress, currentInterface)) { qDebug() 未指定本地端口,加入组播成功; return 1; } else { qDebug() 未指定本地端口,加入组播失败; return -1; } } else { qDebug() 未指定本地端口,绑定失败; return -1; } } else { if (socket-bind(localAddress, localPort, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint)) { socket-setMulticastInterface(currentInterface); if (socket-joinMulticastGroup(targetAddress, currentInterface)) { qDebug() 指定本地端口,加入组播成功; return 1; } else { qDebug() 指定本地端口,加入组播失败; return -1; } } else { qDebug() 指定本地端口,绑定失败; return -1; } } } else { qDebug() 目标地址不是组播地址; return -1; } // 如果执行到这里说明没有通过任何返回语句应该是一个错误 qDebug() init_group 函数执行路径错误; return -1; // 或者抛出异常取决于您希望如何处理这种情况 }1、QUdpSocket::ShareAddress这个选项告诉操作系统允许多个QUdpSocket对象绑定到同一个地址和端口上。在默认情况下操作系统可能会阻止多个 socket 绑定到相同的地址和端口但是如果设置了ShareAddress选项Qt 会尝试在可能的情况下共享这个地址和端口。在实际应用中如果需要多个QUdpSocket对象同时监听相同的地址和端口可以使用ShareAddress选项来避免绑定失败的问题。想象一下你和朋友们想在同一个电话号码上收发短信。默认情况下操作系统可能会阻止多个程序或者多个QUdpSocket对象同时使用同一个网络地址IP 地址和端口号。但是如果你打开了ShareAddress选项就像是你和朋友们一起共享一个电话号码大家可以同时收发信息。这个选项让多个QUdpSocket对象可以在同一个网络地址和端口上工作而不会相互干扰或者造成绑定失败的问题。2、QUdpSocket::ReuseAddressHint这个选项告诉操作系统允许在 UDP Socket 关闭之后立即重新使用相同的地址和端口。如果没有设置这个选项操作系统会在QUdpSocket关闭后一段时间内保持端口的占用状态这可能会导致稍后尝试重新绑定失败。设置ReuseAddressHint可以避免在关闭一个QUdpSocket后立即重新绑定时遇到Address already in use的错误。想象一下你用一个电话号码打电话然后挂了电话。在一段时间内电话号码可能会被暂时保留不允许其他人再用。这就好比默认情况下当一个QUdpSocket关闭后操作系统可能会暂时保留使用的网络地址和端口号不让其他程序立即使用。如果你设置了ReuseAddressHint就像是告诉操作系统“我关掉电话后如果其他人想用这个电话号码可以立刻用不用等。” 这个选项允许在一个QUdpSocket关闭后立即重新使用相同的网络地址和端口号而不会遇到“地址已经被占用”的错误。总结使用后可以重复ip和端口7、发送消息socket-writeDatagram(str,targetAddress,targetPort);8、接收消息//接收消息 connect(udpSocket,QUdpSocket::readyRead,this,[](){ QByteArray datagram; datagram.resize(udpSocket-pendingDatagramSize()); udpSocket-readDatagram(datagram.data(), datagram.size()); ui-recvTextEdit-append(datagram); });9、退出组播int MainWindow::exit_group(QUdpSocket *socket ,QNetworkInterface currentInterface, QHostAddress targetAddress) { if(socket-leaveMulticastGroup(targetAddresscurrentInterface)){ socket-abort(); qDebug() 退出组播成功; return 1; }else{ qDebug() 退出组播失败; return -1; } }四、多网卡场景下组播bind与setMulticastInterface的必要性在多网卡情况下只要bind绑定了IP地址和joinMulticastGroup加入了组播就用不上setMulticastInterface设置网卡函数那为什么有setMulticastInterface函数作用是什么呢场景1已绑定具体IP接口已确定QUdpSocket socket; QNetworkInterface eth0 QNetworkInterface::interfaceFromName(eth0); QHostAddress eth0IP eth0.addressEntries().first().ip(); // 绑定具体IP地址 socket.bind(eth0IP, 12345, QUdpSocket::ShareAddress); // 已指定接口 // 加入组播组 socket.joinMulticastGroup(QHostAddress(224.1.1.1)); // ✅ 不需要setMulticastInterface场景2绑定所有接口但需指定接收接口QUdpSocket socket; // 绑定所有接口 socket.bind(QHostAddress::Any, 12345, QUdpSocket::ShareAddress); // 加入组播组未指定接口 socket.joinMulticastGroup(QHostAddress(224.1.1.1)); // ❌ 系统可能随机选择接口 // ✅ 需要使用setMulticastInterface指定接口 socket.setMulticastInterface(eth0);五、得到当前真实网卡IPQString Widget::get_current_IP() { QListQNetworkInterface interfaceList QNetworkInterface::allInterfaces(); foreach (QNetworkInterface nif, interfaceList) { // 检查网卡是否有效并已经启用 if (nif.isValid() nif.flags().testFlag(QNetworkInterface::IsUp) nif.type() QNetworkInterface::Ethernet (nif.humanReadableName().contains(Realtek, Qt::CaseInsensitive) || nif.humanReadableName().contains(以太网, Qt::CaseInsensitive))) { // 找出对应的IP QListQNetworkAddressEntry entries nif.addressEntries(); foreach (QNetworkAddressEntry entry, entries) { if (entry.ip().protocol() QAbstractSocket::IPv4Protocol) { ui-nif_config-setText(entry.ip().toString()); return entry.ip().toString(); qDebug() Interface: nif.name() IP: entry.ip().toString(); } } } } return ; }ai方案QString Widget::get_current_IP() { QStringList validIps; const QListQNetworkInterface interfaces QNetworkInterface::allInterfaces(); for (const QNetworkInterface interface : interfaces) { // 1. 跳过非物理网卡或非以太网类型的接口 if (!interface.isValid() || interface.type() ! QNetworkInterface::Ethernet || interface.hardwareAddress().isEmpty()) { // 硬件地址为空可能是虚拟网卡 continue; } // 2. 确认网卡状态已启用、未回环、已连接 const bool isUp interface.flags().testFlag(QNetworkInterface::IsUp); const bool isRunning interface.flags().testFlag(QNetworkInterface::IsRunning); // 关键检查物理连接状态 const bool isLoopback interface.flags().testFlag(QNetworkInterface::IsLoopBack); if (!isUp || !isRunning || isLoopback) { continue; } // 3. 只处理有线以太网接口 if (interface.type() ! QNetworkInterface::Ethernet) { continue; } // 4. 获取所有IPv4地址已确认是有线连接 for (const QNetworkAddressEntry entry : interface.addressEntries()) { const QHostAddress ip entry.ip(); // 5. 只处理IPv4地址并排除特殊地址 if (ip.protocol() ! QAbstractSocket::IPv4Protocol || ip.isLoopback() || ip.isMulticast() || ip.isLinkLocal()) { // 169.254.x.x continue; } const QString ipStr ip.toString(); validIps ipStr; qDebug() Found wired IP: ipStr on interface: interface.name() ( interface.hardwareAddress() ); } } // 6. 返回处理逻辑 if (!validIps.isEmpty()) { // 优选公共IP地址 for (const QString ipStr : validIps) { QHostAddress addr(ipStr); if (addr.isGlobal()) { return ipStr; // 优先返回公共IP } } // 没有公共IP时返回第一个找到的IP return validIps.first(); } return QString(); // 返回空字符串表示未找到 }六、动态IP 定时器刷新1、创建定时器timer new MyTimer; timer-start(FLASH_NETWORK_TIME); connect(timer,MyTimer::timeout,this,MainWindow::slt_scanNetwork);2、实现函数 slt_scanNetworkvoid MainWindow::slt_scanNetwork() { //----------有网口热插拔---------- quint8 validInterFaceCount 0;//有效网卡数量 // 获取所有网络接口 QListQNetworkInterface interfaces QNetworkInterface::allInterfaces(); foreach (QNetworkInterface nif, interfaces) { // 检查网卡是否有效并已经启用 if (nif.isValid() nif.flags().testFlag(QNetworkInterface::IsUp)) { validInterFaceCount; } } if(validInterFaceCount netlist.size()){ return; }else{ QString old ui-netInfCombo-currentText(); emit sgl_flashNet(netlist,ui-netInfCombo); if(old.isEmpty()){ return; } if(ui-netInfCombo-findText(old) ! -1){ ui-netInfCombo-setCurrentText(old); }else{ emit sgl_exitUniCast(); } // 有本地IP就加载到本地IP上 if(ui-netInfCombo-findText(get_current_IP()) ! -1){ ui-netInfCombo-setCurrentText(get_current_IP()); } } }3、实现刷新网口函数 init_or_flash_NetInterfaceInfoToComboxconnect(this,Widget::sgl_flashNet,this,[](QListQNetworkInterface list, QComboBox *combox){ myUdpTool-init_or_flash_NetInterfaceInfoToCombox(list,combox); });void MyUdpTools::init_or_flash_NetInterfaceInfoToCombox(QListQNetworkInterface list, QComboBox *combox) { if(!list.isEmpty()){ list.clear(); } if(combox-count() ! 0){ combox-clear(); } QListQNetworkInterface interfaceList QNetworkInterface::allInterfaces(); foreach (QNetworkInterface nif, interfaceList) { // 检查网卡是否有效并已经启用 if (nif.isValid() nif.flags().testFlag(QNetworkInterface::IsUp)) { // 将已经启用的网卡名称添加到列表中 list.append(nif); QListQNetworkAddressEntry entries nif.addressEntries(); foreach (QNetworkAddressEntry entry, entries) { if (entry.ip().protocol() QAbstractSocket::IPv4Protocol) { combox-addItem(entry.ip().toString()); } } } } }七、自定义UDP工具类MyUdpTools.h#ifndef MYUDPTOOLS_H #define MYUDPTOOLS_H #include QObject #include QThread #include QComboBox #include QUdpSocket #include QMessageBox #include QApplication #include QNetworkDatagram #include QNetworkInterface class MyUdpTools : public QObject { Q_OBJECT public: explicit MyUdpTools(QObject *parent nullptr); ~MyUdpTools(); // 单播相关方法 int setupUnicast(QString currentNetInterfacrAddress, quint16 port 0); void sendUnicastData(QString targetAddress,quint16 port,const QByteArray data); int exitUnicast(); // 组播相关方法 int setupMulticast(QNetworkInterface netInterface,QString currentNetInterfacrAddress,QString groupAddress, quint16 port 0); void sendMulticastData(QString targetAddress,quint16 port,const QByteArray data); int exitMulticast(); // 广播相关方法 int setupBroadcast(QString currentNetInterfacrAddress,QString targetAddress,quint16 port 0); void sendBroadcastData(QString targetAddress,quint16 port,const QByteArray data); int exitBroadcast(); //添加或刷新combox的网卡信息 void init_or_flash_NetInterfaceInfoToCombox(QListQNetworkInterface list,QComboBox *combox); signals: void sig_RecvData(QByteArray data); private slots: void processPendingDatagrams();//接收数据槽函数 void handleSocketError(QAbstractSocket::SocketError socketError);//Udp错误日志 private: QThread *thread; QHostAddress localAddress;//本机当前网卡ip QUdpSocket *unicastSocket;//单播 quint16 unicastPort_Local;//单播本地端口 quint16 unicastPort_Target;//单播目标端口 QHostAddress targetAddress_Unicast;//单播目标地址 QUdpSocket *multicastSocket;//组播 QHostAddress multicastGroupAddress;//组播地址 quint16 multicastPort_Local;//组播本地端口 quint16 multicastPort_Target;//组播目标端口 QUdpSocket *broadcastSocket;//广播 QHostAddress broadcastGroupAddress;//广播地址 quint16 broadcastPort_Local;//广播本地端口 quint16 broadcastPort_Target;//广播目标端口 }; #endif // MYUDPTOOLS_HMyUdpTools.cpp#include myudptools.h MyUdpTools::MyUdpTools(QObject *parent) : QObject(parent),unicastSocket(nullptr),multicastSocket(nullptr),broadcastSocket(nullptr) { qInfo() 主线程: QThread::currentThreadId(); qRegisterMetaTypeQNetworkInterface(QNetworkInterface); thread new QThread(); moveToThread(thread); //应用程序关闭后触发 connect(qApp,QApplication::aboutToQuit,thread, QThread::quit); //线程退出后触发 connect(thread, QThread::finished, thread, QThread::deleteLater); connect(thread,QThread::finished,this,MyUdpTools::deleteLater); // 在子线程中直接调用函数 // QMetaObject::invokeMethod(this, init_or_flash_NetInterfaceInfoToCombox, Qt::QueuedConnection); //启动线程 thread-start(); } MyUdpTools::~MyUdpTools() { if(unicastSocket){ delete unicastSocket; } if(multicastSocket){ delete multicastSocket; } if(broadcastSocket){ delete broadcastSocket; } } //单播相关方法实现 int MyUdpTools::setupUnicast(QString currentNetInterfacrAddress, quint16 port) { if (!unicastSocket) { connect(unicastSocket, QUdpSocket::readyRead, this, MyUdpTools::processPendingDatagrams); unicastSocket new QUdpSocket(this);//将父对象设置为当前对象 connect(unicastSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(handleSocketError(QAbstractSocket::SocketError))); qInfo() 创建单播socket; } localAddress QHostAddress(currentNetInterfacrAddress); unicastPort_Local port; if(port 0){ if (!unicastSocket-bind(localAddress,QUdpSocket::ReuseAddressHint|QUdpSocket::ShareAddress)) { qCritical() 单播绑定失败: unicastSocket-errorString(); return -1; } }else{ if (!unicastSocket-bind(localAddress, unicastPort_Local,QUdpSocket::ReuseAddressHint|QUdpSocket::ShareAddress)) { qCritical() 单播绑定失败: unicastSocket-errorString(); return -1; } } qInfo() 单播连接成功; return 1; } void MyUdpTools::sendUnicastData(QString targetAddress, quint16 port, const QByteArray data) { if(unicastSocket){ targetAddress_Unicast QHostAddress(targetAddress); unicastPort_Target port; if(unicastSocket-writeDatagram(data,targetAddress_Unicast,unicastPort_Target) -1){ qCritical() 发送单播数据失败: unicastSocket-errorString(); } } } int MyUdpTools::exitUnicast() { if (unicastSocket) { unicastSocket-close(); unicastSocket nullptr; qCritical() 退出成功; return 1; }else{ qCritical() 已退出单播无需再退出; return -1; } } //组播相关方法实现 int MyUdpTools::setupMulticast(QNetworkInterface netInterface,QString currentNetInterfacrAddress, QString groupAddress, quint16 port) { if(!multicastSocket){ multicastSocket new QUdpSocket(this); connect(multicastSocket, QUdpSocket::readyRead, this, MyUdpTools::processPendingDatagrams); connect(multicastSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(handleSocketError(QAbstractSocket::SocketError))); qInfo() 创建组播socket; } localAddress QHostAddress(currentNetInterfacrAddress); multicastGroupAddress QHostAddress(groupAddress); multicastPort_Local port; if(!multicastGroupAddress.isMulticast()){ qWarning() 不是组播地址; return -1; } if(port 0){ if(!multicastSocket-bind(localAddress,QUdpSocket::ReuseAddressHint|QUdpSocket::ShareAddress)){ qCritical() 组播绑定失败: multicastSocket-errorString(); return -1; } }else{ if(!multicastSocket-bind(localAddress,multicastPort_Local,QUdpSocket::ReuseAddressHint|QUdpSocket::ShareAddress)){ qCritical() 组播绑定失败: multicastSocket-errorString(); return -1; } } multicastSocket-setMulticastInterface(netInterface); if(!multicastSocket-joinMulticastGroup(multicastGroupAddress,netInterface)){ qCritical() 加入组播失败: multicastSocket-errorString(); return -1; } qInfo() 组播连接成功; return 1; } void MyUdpTools::sendMulticastData(QString targetAddress, quint16 port, const QByteArray data) { if(multicastSocket){ multicastGroupAddress QHostAddress(targetAddress); multicastPort_Target port; if(multicastSocket-writeDatagram(data,multicastGroupAddress,multicastPort_Target) -1){ qCritical() 发送组播数据失败: multicastSocket-errorString(); } } } int MyUdpTools::exitMulticast() { if (multicastSocket) { multicastSocket-leaveMulticastGroup(multicastGroupAddress); // 确保在退出组播后关闭套接字 multicastSocket-close(); // 删除套接字指针 multicastSocket nullptr; qInfo() 退出组播成功; return 1; }else{ qCritical() 已退出组播,无需再退出; return -1; } } //广播相关方法实现 int MyUdpTools::setupBroadcast(QString currentNetInterfaceAddress,QString targetAddress, quint16 port) { if (!broadcastSocket) { broadcastSocket new QUdpSocket(this);//将父对象设置为当前对象 connect(broadcastSocket, QUdpSocket::readyRead, this, MyUdpTools::processPendingDatagrams); connect(broadcastSocket, SIGNAL(error(QAbstractSocket::SocketError)),this, SLOT(handleSocketError(QAbstractSocket::SocketError))); qInfo() 创建广播socket; } localAddress QHostAddress(currentNetInterfaceAddress); broadcastPort_Local port; broadcastGroupAddress QHostAddress(targetAddress); if(!broadcastGroupAddress.isBroadcast()){ qCritical() 不是广播地址; return -1; } if(port 0){ if (!broadcastSocket-bind(localAddress,QUdpSocket::ReuseAddressHint|QUdpSocket::ShareAddress)) { qCritical() 广播绑定失败: broadcastSocket-errorString(); return -1; } }else{ if (!broadcastSocket-bind(localAddress, broadcastPort_Local,QUdpSocket::ReuseAddressHint|QUdpSocket::ShareAddress)) { qCritical() 广播绑定失败: broadcastSocket-errorString(); return -1; } } qInfo() 连接成功; return 1; } void MyUdpTools::sendBroadcastData(QString targetAddress, quint16 port, const QByteArray data) { if(broadcastSocket){ broadcastGroupAddress QHostAddress(targetAddress); broadcastPort_Target port; if(broadcastSocket-writeDatagram(data,broadcastGroupAddress,broadcastPort_Target) -1){ qCritical() 发送广播数据失败: broadcastSocket-errorString(); } } } int MyUdpTools::exitBroadcast() { if (broadcastSocket) { broadcastSocket-close(); broadcastSocket nullptr; qInfo() 退出成功; return 1; }else{ qDebug() 已退出广播,无需再退出; return -1; } } //添加或刷新combox的网卡信息函数实现 void MyUdpTools::init_or_flash_NetInterfaceInfoToCombox(QListQNetworkInterface list, QComboBox *combox) { list.clear(); combox-clear(); QListQNetworkInterface interfaceList QNetworkInterface::allInterfaces(); foreach (QNetworkInterface nif, interfaceList) { // 检查网卡是否有效并已经启用 if (nif.isValid() nif.flags().testFlag(QNetworkInterface::IsUp)) { // 将已经启用的网卡名称添加到列表中 list.append(nif); QListQNetworkAddressEntry entries nif.addressEntries(); foreach (QNetworkAddressEntry entry, entries) { if (entry.ip().protocol() QAbstractSocket::IPv4Protocol) { combox-addItem(entry.ip().toString()); } } } } } //接收数据槽函数实现 void MyUdpTools::processPendingDatagrams() { QUdpSocket *socket qobject_castQUdpSocket *(sender());//sender()发送信号的对象的指针 if (!socket) return; while (socket-hasPendingDatagrams()) { #if 1 QByteArray datagram; datagram.resize(socket-pendingDatagramSize()); socket-readDatagram(datagram.data(), datagram.size()); qDebug() Received Data: datagram; emit sig_RecvData(datagram); #else QNetworkDatagram datagram socket-receiveDatagram(); QByteArray data datagram.data(); qDebug() Received Data: datagram; emit sig_RecvData(data); #endif } } //Udp错误日志信息 void MyUdpTools::handleSocketError(QAbstractSocket::SocketError socketError) { QUdpSocket *socket qobject_castQUdpSocket *(sender()); if (!socket) return; switch (socketError) { case QAbstractSocket::HostNotFoundError: qCritical() Host not found error: socket-errorString(); break; case QAbstractSocket::ConnectionRefusedError: qCritical() Connection refused error: socket-errorString(); break; case QAbstractSocket::DatagramTooLargeError: qCritical() Datagram too large error: socket-errorString(); break; default: qCritical() Socket error: socket-errorString(); break; } }注意为什么new QThread();不能指定父对象为this如果指定父对象为this的话QThread的生命周期就由其父对象MyUdpTools管理不是由QThread直接管理这不是推荐的做法。不同线程之间的通信要用信号与槽进行通信。在子线程中不能调用函数会影响线程安全可以在主线程中使用函数QMetaObject::invokeMethod 使得 子线程能直接调用函数。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门