使用多线程在QT中实现UDP接收
使用多线程的方式,实现UDP数据的接收,并将接收到的UDP数据打印到textEdit当中。
今天这个demo主要是使用socket绑定一个固定的IP个端口接收UDP数据,使用moveToThread的方式将UDP的接收放到子线程里面。
废话不多说,直接开始:
步骤1:界面设计,这里其实只需要绑定自己的IP和接收端口就好了,我主要是想要后面接着利用这个demo写其他的东西,所以就多添加了写组件,注意要添加一个textEdit来显示接收到的数据。
步骤二:创建一个ReceiveClass.cpp和ReceiveClass.h来实现咱们的UDP接收class:
ReceiveClass.h
#ifndef RECEIVECLASS_H
#define RECEIVECLASS_H
#include <QObject>
#include <QUdpSocket>
class ReceiveClass : public QObject
{
Q_OBJECT
public:
explicit ReceiveClass(QObject *parent = nullptr) : QObject(parent) {}
void ReceiveSocketInif(QString ip,quint16 port);
//定义槽函数
public slots:
void startReceiving();
//定义信号
signals:
//UDP接收到数据信号
void datagramReceived(QByteArray data);
//日志输出信号
void UdpRcvLogSingal(const QString& text);
//定义槽处理函数
private slots:
void processPendingDatagrams();
private:
QUdpSocket *socket;
};
#endif // RECEIVECLASS_H
ReceiveClass.cpp
#include "ReceiveClass.h"
bool socketInitFlag = false;
void ReceiveClass::ReceiveSocketInif(QString ip,quint16 port)
{
QString log = "";
bool ret = false;
// 创建QUdpSocket对象
socket = new QUdpSocket(this);
QHostAddress IP(ip);
ret = socket->bind(IP, port);
if(ret == true)
{
socketInitFlag = true;
log += "Socket初始化成功\n";
}
else
{
socketInitFlag = false;
log += "Socket初始化失败\n";
}
emit UdpRcvLogSingal(log);
}
void ReceiveClass::startReceiving()
{
QString log = "";
//当前socket已经初始化完成
if((socketInitFlag == true) && (nullptr != socket ))
{
connect(socket, &QUdpSocket::readyRead, this, &ReceiveClass::processPendingDatagrams);
}
else
{
log += "接收socket未初始化未完成\n";
}
emit UdpRcvLogSingal(log);
}
//收到数据后完成数据读取连接信号
void ReceiveClass::processPendingDatagrams()
{
while (socket->hasPendingDatagrams())
{
QByteArray datagram;
datagram.resize(socket->pendingDatagramSize());
//将信息读取完成
socket->readDatagram(datagram.data(), datagram.size());
//传递信号
emit datagramReceived(datagram);
}
}
步骤3:在自己的主窗口文件中实现几个槽函数,日志输出槽函数和UDP接收处理槽函数
函数实现
//接收数据槽处理函数
void MainWindow::handleReceivedDatagram(QByteArray data)
{
QString str = "";
const uint8_t* receiveBuffer = reinterpret_cast<const uint8_t*>(data.constData());
for(size_t i=0; i< data.size(); i++)
{
//将接收到的数据按照16进制的方式显示
str += QString("%1").arg(static_cast<quint8>(receiveBuffer[i]), 2, 16, QLatin1Char('0'));
str += " ";
}
//将接收到的数据打印
MainWindow::LogPrint(str);
}
//日志输出
void MainWindow::LogPrint(const QString& text)
{
ui->textEdit->append(text);
}
在绑定按钮的点击槽函数实现socket的初始化和线程处理
//绑定按钮点击
void MainWindow::on_pushButton_2_clicked()
{
QString srcip = ui->lineEdit->text();
quint16 srcRcvPort = ui->lineEdit_4->text().toInt();
quint16 srcSedPort = ui->lineEdit_3->text().toInt();
QString dstip = ui->lineEdit_2->text();
quint16 dstRcvPort = ui->lineEdit_6->text().toInt();
quint16 dstSedPort = ui->lineEdit_5->text().toInt();
//绑定接收端口
receiveClass->ReceiveSocketInif(srcip,srcRcvPort);
//将uDPReceiver移动到udpReceiveThread线程中执行
receiveClass->moveToThread(udpReceiveThread);
//连接线程线程开始信号到UDP接收
connect(udpReceiveThread, &QThread::started, receiveClass, &ReceiveClass::startReceiving);
//收到数据后连接到数据处理槽函数
connect(receiveClass, &ReceiveClass::datagramReceived, this, &MainWindow::handleReceivedDatagram);
udpReceiveThread->start();
}
然后注意在绑定测试的时候,注意在自己电脑上需要添加自己的IP,不然绑定会失败。
测试效果:
作者:zhouzhouyaonvli