Bootstrap

QT下的UDP通信

QT中使用UDP通信

本例程已在单台机器和路由器下的多台机器上测试成功。
只支持文本发送,如果特殊的数据类型比如结构体、表格发送还得另定义编码与解析。
代码在文章末尾。点击跳转

发送端构建思路:

1、引入与UDP通信相关的库和功能;
2、头文件中声明通信相关的对象udpSocket和按键绑定函数等;
3、定义函数内容:
[3.1]按键绑定函数中为读取目标端口、目标IP与发送内容的功能;
[3.2]再用UdpSocket库的功能进行发送。

接收端构建思路:

[跳转到接收端]
1、引入与UDP通信相关的库和功能;
2、头文件中声明通信相关的对象udpSocket、按键绑定函数和接收处理函数等;
3、定义函数内容:
[3.1]读取端口号与IP地址,绑定端口,验证端口绑定成功;
[3.2]定义接收处理函数,与事件“接收到信号”进行绑定,每次接收到数据便执行此函数。

发送端

发送端的ui设计如下

page1

其中,上方的PlainTextEdit组件是数据发送的输入框;下方的PlainTextEdit组件是信息输出框;
端口号输入框使用QSpinBox组件,IP输入框使用QLineEdit组件。
具体实现代码我会放在文末,[点击跳转]下面对关键部分代码做以下解释。

首先,UDP通信是QT安装时自带的,但是新建的QT工程并没有引入,需要在.pro工程文件中加入下面代码引入UDP通信功能:

QT += network

这样,在头文件中就可以包含与UDP通信相关的库了:

#include <QUdpSocket>

头文件内容如下:

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    void on_pushButton_clicked();

private:
    Ui::Widget *ui;
    QUdpSocket *udpSocket;//
    QString getLocalIP();//获取本机IP地址
};

#endif // WIDGET_H

其中,有一个按键的绑定函数,一个QUdpSocket的指针对象,还有一个获取本机IP的函数。

构造函数如下,为udpSocket指针对象new一块空间:

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    QString localIP=getLocalIP();//本机IP
    this->setWindowTitle(this->windowTitle()+"----本机IP:"+localIP);

    udpSocket=new QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocket
}

析构函数如下,断开udp通信以及手动删除new的对象:

Widget::~Widget()
{
    udpSocket->abort();
    delete udpSocket;
    delete ui;
}

按键绑定函数的定义如下,可以根据自身需要修改:

void Widget::on_pushButton_clicked()
{
    QString targetIP=ui->lineEdit->text();
    QHostAddress targetAddr(targetIP);  //目标IP

    quint16 targetPort = ui->spinBox->value();  //目标端口号
    QString msg = ui->plainTextEdit->toPlainText(); //发送内容
    QByteArray str = msg.toUtf8();

    udpSocket->writeDatagram(str, targetAddr, targetPort);  //发送函数

    ui->window->appendPlainText("[send]" + msg);   //信息栏显示

    ui->plainTextEdit->clear();
    ui->plainTextEdit->setFocus();
}

获取本机IP函数:

QString Widget::getLocalIP()
{
    QString hostName=QHostInfo::localHostName();//本地主机名
    QHostInfo   hostInfo=QHostInfo::fromName(hostName);
    QString   localIP="";

    QList<QHostAddress> addList=hostInfo.addresses();//

    if (!addList.isEmpty())
    for (int i=0;i<addList.count();i++)
    {
        QHostAddress aHost=addList.at(i);
        if (QAbstractSocket::IPv4Protocol==aHost.protocol())
        {
            localIP=aHost.toString();
            break;
        }
    }
    return localIP;
}

接收端

接收端ui设计如下:

pege2

其中,目标端口使用的是QSpinBox组件,目标IP使用的是QLineEdit组件,接受框用PlainTextEdit组件。

同样在.pro文件中加入network模块

QT += network

头文件内容加入:

#include <QUdpSocket>

头文件内容如下

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget();

private:
    Ui::Widget *ui;
    QUdpSocket *udpSocket;//
    QString getLocalIP();//获取本机IP地址

private slots:
    void onSocketReadyRead();//读取socket传入的数据
    void on_pushButton_clicked();
};
#endif // WIDGET_H

与发送端的构成类似,信号函数多了一个读取socket传入的数据函数:void onSocketReadyRead()

各个函数的定义如下:

构造函数如下

由于接收端需要适时监听端口号收到的信息,因此需要将readyRead()信号与自定义的信号读取函数onSocketReadyRead()关联。

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);

    QString localIP=getLocalIP();//本机IP
    this->setWindowTitle(this->windowTitle()+"----本机IP:"+localIP);

    udpSocket=new QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocket
    connect(udpSocket,SIGNAL(readyRead()),
            this,SLOT(onSocketReadyRead()));
//    udpSocket=new QUdpSocket(this);//用于与连接的客户端通讯的QTcpSocket
//    connect(udpSocket,SIGNAL(readyRead()),
//            this,SLOT(onSocketReadyRead()));
}

析构函数如下

Widget::~Widget()
{
    udpSocket->abort();
    delete udpSocket;
    delete ui;
}

要监听端口来接受信息,首先要对端口号进行绑定,当udpSocket->bind(port)返回true时表示端口绑定成功

void Widget::on_pushButton_clicked()
{
    quint16 port = ui->spinBox->value(); //本机UDP端口
    if (udpSocket->bind(port)) //绑定端口成功
    {
        ui->plainTextEdit->appendPlainText("**成功绑定端口");
        ui->plainTextEdit->appendPlainText("**绑定端口:" +
                QString::number(udpSocket->localPort()));    }
}

socket读取函数如下,当端口监听到有数据发来,就会执行此函数

void Widget::onSocketReadyRead()
{
    while(udpSocket->hasPendingDatagrams())
    {
        QByteArray datagram;
        datagram.resize(udpSocket->pendingDatagramSize());  //数据格式统一

        QHostAddress peerAddr;
        quint16 peerPort;
        udpSocket->readDatagram(datagram.data(),
                                datagram.size(),&peerAddr,&peerPort);   //接收数据
        QString str=datagram.data();    //数据转换为QT的ui界面使用的QString类型

        QString peer="[From "+peerAddr.toString()+":"
                +QString::number(peerPort)+"] ";

        ui->plainTextEdit->appendPlainText(peer+str);
    }
}

获取ip函数与发送端相同

QString Widget::getLocalIP()
{
    QString hostName=QHostInfo::localHostName();//本地主机名
    QHostInfo   hostInfo=QHostInfo::fromName(hostName);
    QString   localIP="";

    QList<QHostAddress> addList=hostInfo.addresses();//

    if (!addList.isEmpty())
    for (int i=0;i<addList.count();i++)
    {
        QHostAddress aHost=addList.at(i);
        if (QAbstractSocket::IPv4Protocol==aHost.protocol())
        {
            localIP=aHost.toString();
            break;
        }
    }
    return localIP;
}

演示

在这里插入图片描述

IP查询可以cmd到终端输入ipconfig查询。
端口号绑定不成功的话换一个数字重试,可能与计算机已经占用的端口发生了冲突,一般选择时尽量选择大一些的端口号避免冲突。
不要开串口调试工具同时进行调试,使用时应该断开串口调试共苦,原因就是上一点的串口冲突问题。
单机测试使用时,开两个QT应用,发送和接收程序都运行,先根据自己电脑的IP地址设置号IP,端口号保持一致。在接收端点击绑定端口,端口绑定成功后会有提示,再到发送端发送自己像发送的内容,图片中发送的Hello已经成功接收到。
多机使用时,保证IP处于同一网段,接收端和发送端端口设置一致,IP使用接收端电脑的IP地址。

代码地址:

❤️跳转过来啦❤️

百度云

链接:https://pan.baidu.com/s/1VrwGKHWd2RRv_PNu7tUZvg
提取码:pkci

Github

https://github.com/FLBa9762/Qt-UdpSocket-Code.git

;