Qt实现基于线程的UDP通信

基于窗口部件的UDP通信实现,虽然简单易用,但是窗口部件主要的工作是负责处理大量的用户界面信息,当有耗时的处理过程时,会影响数据的接收,造成丢帧。这里使用的是独立的线程负责网络数据的发送和接收,再通过窗口部件显示出来,在实时系统中应用特别广泛。下面是基于线程实现UDP通信的列程。

新建工程,在工程中新建发送和接收的C++类,sendthread.h,sendthread.cpp,recvthread.h,recvthread.cpp。

(1)sendthread.h定义如下:

#ifndef SENDTHREAD_H
#define SENDTHREAD_H
#include <QThread>
#include <QtNetwork/QNetworkInterface>
#include <QtNetwork/QHostAddress>
#include <QtNetwork/QUdpSocket>

/*添加网络数据报文的结构定义*/
#pragma pack(push) //保存对齐状态
#pragma pack(2)    //设定为4字节对齐
struct DataStruct{ 
    unsigned short frame;
    unsigned short index;
    unsigned short year;
    unsigned short month;
    unsigned short day;
    unsigned short end;
};
//NetBuffer联合提供了一种访问与DataStruct结构大小为12的字符数组相同的内存空间的方法
union NetBuffer{
    DataStruct data;
    char dataBuffer[12];
};
#pragma pack(pop) //恢复字节对齐状态

/******************************************************************************************/
/******************************************************************************************/
class SendThread : public QThread
{
    Q_OBJECT
public:
    explicit SendThread(QWidget * parent=0);
protected:
    void run();//定义线程需要重载的run()操作
private:
    /*定义线程需要用到的主机地址hostAddress,UDPsocke端口和发送缓冲区NetBuffer*/
    QHostAddress hostAddress;
    QUdpSocket udpSendSocket;
    NetBuffer sendBuffer;
};

#endif // SENDTHREAD_H

(2)sendthread.cpp定义如下:

#include "sendthread.h"
#include "mainwindow.h"
#include <QTime>
#include <QList>
/*初始化参数,获取本机地址,绑定socket端口*/
SendThread::SendThread(QWidget * parent):
    QThread(parent)
{
    QList<QHostAddress>addressList=QNetworkInterface::allAddresses();
    hostAddress=addressList.at(0);
    udpSendSocket.bind(hostAddress,3407);
    sendBuffer.data.index=0;
}
/*重载实现run()操作*/
void SendThread::run()
{
    while(true) {
        //QTime tm = QTime::currentTime();
        sendBuffer.data.frame = 0x0ff0;
        sendBuffer.data.index++;
        sendBuffer.data.year = 2023;
        sendBuffer.data.month = 9;
        sendBuffer.data.day = 1;
        sendBuffer.data.end = 0x0ee0;
        udpSendSocket.writeDatagram(sendBuffer.dataBuffer,sizeof(sendBuffer),hostAddress,3408);

        QString displayString;
        displayString = QString("帧头 = %1 \n 帧计数 = %2 \n 年 = %3 \n 月 = %4 \n 日 = %5 \n 帧尾 = %6 \n")
               /*第一个参数是要输出的值包括字符串和各种基本类型的值,第二个参数是字段宽度,
                * 第三个参数一般是用2、8、10、16表示进制,最后一个参数是填充字符
                */
                .arg(sendBuffer.data.frame,2,16)
                .arg(sendBuffer.data.index,2,10)
                .arg(sendBuffer.data.year,2,10)
                .arg(sendBuffer.data.month,2,10)
                .arg(sendBuffer.data.day,2,10)
                .arg(sendBuffer.data.end,2,16);
        ((MainWindow*)this->parent())->DisplaySendData(displayString);
        this->sleep(1);
    }
}

(3) recvthread.h定义如下:

#ifndef RECVTHREAD_H
#define RECVTHREAD_H

#include "sendthread.h"
//UDP通信需要定义的头文件
#include <QThread>
#include <QtNetwork/QNetworkInterface>
#include <QtNetwork/QHostAddress>
#include <QtNetwork/QUdpSocket>

class RecvThread:public QThread
{
    Q_OBJECT
public:
    explicit RecvThread(QWidget * parent=0);
private:
    /*定义线程需要用到的主机地址hostAddress,UDPsocke端口和发送缓冲区NetBuffer*/
    QHostAddress hostAddress;
    QUdpSocket udpRecvSocket;
    NetBuffer recvBuffer;
protected:
    void run();//定义线程需要重载的run()操作
};

#endif // RECVTHREAD_H

(4) recvthread.cpp定义如下:

#include "recvthread.h"
#include "mainwindow.h"
#include <QList>
#include <QDebug>
/*初始化参数,获取本机地址,绑定socket端口*/
RecvThread::RecvThread(QWidget * parent):QThread(parent)
{
   QList<QHostAddress>addressList=QNetworkInterface::allAddresses();
   hostAddress=addressList.at(0);
   udpRecvSocket.bind(hostAddress,3408);
}

/*
 * 在run()中读取网络数据,并通过主窗口的DisplayRecvData方式显示
 * 这里使用了waiForReadyRead方法,以同步的方式读取数据,而不是使用信号和槽的异步方法
 * 当没有新数据到来时,线程处于挂起等待状态,当有数据到达时,立刻进入下一步处理,这种方式响应的更及时快速
*/
void RecvThread::run()
{
    while(true)
    {
        if(udpRecvSocket.waitForReadyRead())
        {
           QHostAddress sender;
           quint16 senderPort;
           udpRecvSocket.readDatagram(recvBuffer.dataBuffer,sizeof(recvBuffer),&sender,&senderPort);
           QString displayString;
           displayString = QString("帧头 = %1 \n 帧计数 = %2 \n 年 = %3 \n 月 = %4 \n 日 = %5 \n 帧尾 = %6 \n")
                   .arg(recvBuffer.data.frame,2,16)
                   .arg(recvBuffer.data.index,2,10)
                   .arg(recvBuffer.data.year,2,10)
                   .arg(recvBuffer.data.month,2,10)
                   .arg(recvBuffer.data.day,2,10)
                   .arg(recvBuffer.data.end,2,16);
          ((MainWindow*)this->parent())->DisplayRecvData(displayString);
          qDebug("打印输出数据");
        }
    }
}

(5)mainwindow.h定义如下:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

namespace Ui {
class MainWindow;
}


class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
    void DisplayRecvData(QString displayString);
    void DisplaySendData(QString displayString);

private:
    Ui::MainWindow *ui;
};

#endif // MAINWINDOW_H


(6)mainwindow.cpp定义如下:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "sendthread.h"
#include "recvthread.h"

/*在主窗口中,发送和接收socket线程,定义DisplaySendData和DisplayRecvData操作显示收发数据*/
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    SendThread *sendThread = new SendThread(this);
    RecvThread *recvThread = new RecvThread(this);
    recvThread->start();
    sendThread->start();
}

MainWindow::~MainWindow()
{
    delete ui;  
}

void MainWindow::DisplaySendData(QString displayString)
{
    ui->listWidget->insertItem(0,displayString);
}
void MainWindow::DisplayRecvData(QString displayString)
{
    ui->listWidget_2->insertItem(0,displayString);
}

(7)运行结果如图所示:

在这里插入图片描述

(8)源码地址:QT实现基于线程的UDP通信

  • 1
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九点两刻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值