基于Qt多线程实现UDP通信演示:
先演示,在展开如何实现,本次代码在windows、linux、mac都可使用。
大家不能光看,实际敲一敲,敲出强大,敲出好工作。
简单理解:服务器-》发送hello-》客户端
也可以不写客户端或者服务器之一,使用以下调试工具即可。
UDP
由于要使用套接字,所以需要在服务器和客户端的工程文件中都添加:
QT += core gui network
使用writeDatagram方法传输数据,readDatagram方法接收数据。QT在调用writeDatagram方法时候会自动发出readyRead信号给接收方监听。
多线程
Qt有两种多线程的方法,其中一种是继承QThread的run函数,另外一种是把一个继承于QObject的类转移到一个Thread里,即使用MoveToThread。
Qt4.8之前都是使用继承QThread的run这种方法,但是Qt4.8之后,Qt官方建议使用第二种方法。因此本文使用的是第二种方法。
第二种方法主要就是写一个一个继承于QObject的类,将耗时的工作写在该类的槽函数中。
然后将派生类对象移动到一个QThread中,该线程需要start。最后,通过信号连接派生类的槽函数,并通过信号触发槽函数。(槽函数在子线程中执行)。
先把ui界面贴一下,其中label名字叫做label_tosend,按钮名字pushbutton_start。
服务器实现
套接字头文件
#ifndef TRAVEL_H
#define TRAVEL_H
#include <QObject>
#include <QDebug>
#include <QString>
#include <QUdpSocket>
class travel : public QObject
{
Q_OBJECT
public:
explicit travel(QObject *parent = nullptr);
~travel();
signals:
void sig_ok();
public slots:
void slot_do(QString msg,int port);
//主要实现功能的函数,用于传送数据给客户端,其中两个传入参数分别是待传数据和客户端端口号
private:
QUdpSocket *mudpsocket;
};
#endif // TRAVEL_H
套接字源文件
// 接下来是traval.cpp
#include "travel.h"
#include <QUdpSocket>
#include <QThread>
travel::travel(QObject *parent) : QObject(parent)
{
qDebug()<<"构造了travel";
mudpsocket=new QUdpSocket(this);//新建一个UDP套接字
}
travel::~travel()
{
qDebug()<<"析构了travel";
}
void travel::slot_do(QString msg,int port)
{
//writeDatagram方法传入4个参数,分别是数据,数据大小,接收端ip,接收端端口
//如果传输成功,该方法返回传输数据的大小(字节),如果失败返回-1
int len=mudpsocket->writeDatagram(msg.toUtf8(),msg.length(),QHostAddress::Broadcast,port);
if(len!=msg.length())
{return;}
qDebug()<<"开启线程"<<QThread::currentThreadId();//查看槽函数在哪个线程运行
emit sig_ok();//发出我已经传输完毕的信号
}
服务器udphost.h
#ifndef UDPHOST_H
#define UDPHOST_H
#include <QWidget>
#include "travel.h"
#include <QThread>
#include <QString>
QT_BEGIN_NAMESPACE
namespace Ui { class UdpHost; }
QT_END_NAMESPACE
class UdpHost : public QWidget
{
Q_OBJECT
public:
UdpHost(QWidget *parent = nullptr);
~UdpHost();
signals:
void sig_dowork(QString,int);
private slots:
void on_pushButton_start_clicked();
public slots:
void slot_finish();
private:
Ui::UdpHost *ui;
travel *traveltoclient;
QThread *thread;
};
#endif // UDPHOST_H
这是一个UDP主机的头文件实现,它包括以下功能:
1. 实现了一个继承自QWidget的UdpHost类,用于显示UDP主机的界面。
2. 在构造函数中初始化了界面和相关变量。
3. 定义了一个信号sig_dowork,用于发送工作请求给travel类。
4. 定义了一个槽函数slot_finish,用于处理工作完成的信号。
5. 定义了一个私有成员变量traveltoclient,表示与客户端通信的travel类的实例。
6. 定义了一个私有成员变量thread,表示用于执行travel类的线程。
在界面上,有一个开始按钮,点击该按钮会触发槽函数on_pushButton_start_clicked(),用于发送工作请求给travel类。当工作完成时,会触发槽函数slot_finish(),用于处理工作完成的信号。
通过使用这个头文件,你可以方便地创建一个UDP主机的界面,并实现与客户端的通信功能。
服务器udphost.cpp
#include "udphost.h"
#include "ui_udphost.h"
#include <QDebug>
#include <QString>
UdpHost::UdpHost(QWidget *parent)
: QWidget(parent)
, ui(new Ui::UdpHost)
{
qDebug()<<"主线程:"<<QThread::currentThreadId();
ui->setupUi(this);
traveltoclient=new travel();//创建对象
thread=new QThread();//创建线程
traveltoclient->moveToThread(thread);//使用该方法实现多线程,这是QT推荐的
connect(thread,&QThread::finished,traveltoclient,&QObject::deleteLater);
connect(this,&UdpHost::sig_dowork,traveltoclient,&travel::slot_do);
//用connect的方式调用do函数,否则多线程报错
connect(traveltoclient,&travel::sig_ok,this,&UdpHost::slot_finish);
}
UdpHost::~UdpHost()
{
delete ui;
//关闭子线程
thread->quit();
thread->wait();
}
void UdpHost::on_pushButton_start_clicked()
{
thread->start();
QString msg=ui->label_tosend->text();
emit sig_dowork(msg,6666);//把数据和端口号作为参数传出去
}
void UdpHost::slot_finish()
{
qDebug()<<"结束"<<QThread::currentThreadId();
}
客户端的代码
客户端的ui里就放了一个label用来显示接收到的数据,名字是label_get。
客户端的头文件
#ifndef UDPCLIENT_H
#define UDPCLIENT_H
#include <QWidget>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class udpClient; }
QT_END_NAMESPACE
class udpClient : public QWidget
{
Q_OBJECT
public:
udpClient(QWidget *parent = nullptr);
~udpClient();
public slots:
void slot_received();//用来处理接收到的数据
private:
Ui::udpClient *ui;
QUdpSocket *mudpsocket;
};
#endif // UDPCLIENT_H
这是一个UDP客户端的头文件实现,它包括以下功能:
- 1. 实现了一个继承自QWidget的udpClient类,用于显示UDP客户端的界面。
- 2. 在构造函数中初始化了界面和相关变量。
- 3. 定义了一个槽函数slot_received,用于处理接收到的数据。
- 4. 定义了一个私有成员变量mudpsocket,表示用于进行UDP通信的QUdpSocket类的实例。
在界面上,没有提供发送数据的按钮,因此该UDP客户端主要用于接收数据。当接收到数据时,会触发槽函数slot_received,用于处理接收到的数据。
通过使用这个头文件,你可以方便地创建一个UDP客户端的界面,并实现接收数据的功能。
客户端的CPP代码
#include "udpclient.h"
#include "ui_udpclient.h"
#include <QByteArray>
udpClient::udpClient(QWidget *parent)
: QWidget(parent)
, ui(new Ui::udpClient)
{
ui->setupUi(this);
mudpsocket=new QUdpSocket(this);
mudpsocket->bind(6666);
connect(mudpsocket,&QUdpSocket::readyRead,this,&udpClient::slot_received);
//监听readRead信号
}
udpClient::~udpClient()
{
delete ui;
}
void udpClient::slot_received()
{
while(mudpsocket->hasPendingDatagrams())
{
QByteArray datagram;
//为避免数据丢失,在尝试读取之前,调用pendingDatagramSize()确定未决数据报的大小
datagram.resize(mudpsocket->pendingDatagramSize());
//读取数据,该方法传入四个参数,后面两个可以为空,分别是数据,数据的最大大小,地址和端口
mudpsocket->readDatagram(datagram.data(),datagram.size());
QString msg=datagram.data();
ui->label_get->setText(msg);//显示收到的数据
}
}
作者:Qt历险记
原文:https://mp.weixin.qq.com/s/ZHRFVXmch9FqZqIZNc1noA
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。