欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用Qt的UDP通信协议,实现局域网通信功能(仿组播软件版本--简单)

程序员文章站 2022-05-19 10:51:12
...

1.简介效果

在之前的文章中,介绍了TCP协议在Qt中的应用实现方式传送门,TCP通信方式是面向对象,可靠的连接服务。而与之对应的是不可靠,无连接的UDP通信协议,两者是TCP/IP协议簇中较为常用的两者通信方式。

之前的博文中,实现了利用Qt调用TCP协议的局域网通信聊天,而这篇博文将介绍UDP协议在Qt中的简单运用,其想要达到的效果如下。

使用Qt的UDP通信协议,实现局域网通信功能(仿组播软件版本--简单)

使用UDP就和TCP不同的是,UDP协议只管发,实用于及时通信领域,它只管发送,不管对方是否收到,但因为其传输效率高效,消耗资源小,所以尝尝结合于TCP协议一同使用在通信连接中。我们最常用的就是QQ,QQ采用TCP连接来保持在线状态,使其连接至上层的腾讯服务器,而聊天发送消息功能大都采用UDP方式,当UDP协议不能正常转发的时候,就会采用TCP协议进行发送,所以,TCP和UDP协议常常一同使用。

2.项目设计

1)流程图

UDP协议流程图,如下图所示,经此流程图后,我们可以清晰的知道,UDP是如何通信工作,并进行调用。

使用Qt的UDP通信协议,实现局域网通信功能(仿组播软件版本--简单)

UDP协议没有明确的客户端和服务端可言,只要绑定了对应的IP地址和端口号,就可以进行通信,如很多的组播软件,可以使绑定同一个IP。使得多个端口号的客户在此IP下进行通信和数据传输。

2)项目构建

新建为widget项目,构建如下所示项目树

使用Qt的UDP通信协议,实现局域网通信功能(仿组播软件版本--简单)

3)界面构建

两个界面设计构建如下

  • 界面一 - secondwdiget

使用Qt的UDP通信协议,实现局域网通信功能(仿组播软件版本--简单)

  • 界面二 - widget

使用Qt的UDP通信协议,实现局域网通信功能(仿组播软件版本--简单)
使用行编辑框和文本编辑框构建简单的界面如上所示。

4)代码设计

和Qt使用TCP一样,想要在Qt中使用网络编程的包。需要在.pro工程文件里面添加如下代码

QT       += network

a.widget.h

首先还是需要添加Qt中封装好的UDP的头文件

#include <QUdpSocket>

之后对其进行初始化声明,并定义一个处理消息队列的槽函数,用以在UDP通信套接字连接后发送消息,之后便是重写事件过滤器用以响应键盘Enter键的输入,具体代码如下

public:
	void dealMsg();         //槽函数,处理对方发过来的数据
protected:
    bool eventFilter(QObject *target, QEvent *event);//事件过滤器
private:
    QUdpSocket *udpSocket;

b.widget.cpp

在执行文件中,对构建函数中的udpSocket分配动态空间,当连接好之后,自动并触发连接的槽函数。

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->resize(450,300);
    this->setWindowTitle("服务器端口:8888");
    udpSocket = new QUdpSocket(this);

    //绑定端口号
    udpSocket->bind(8888);
    //udpSocket->bind(QHostAddress::AnyIPv4,8888);        //必须是主机的ipv4类通信方式,以及本机的ip地址
    //当对方发送数据过来。自动触发readyRead()
  connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::dealMsg);
	//键盘Enter键设置
    ui->ButtonSend->setFocus();
    ui->ButtonSend->setDefault(true);
    ui->textEditShow->installEventFilter(this);//设置完后自动调用其eventFilter函数

}

之后便是对自定义处理消息队列的槽函数进行函数实现

void Widget::dealMsg()
{
    //读取对方发送的内容
    char buf[1024] = {0};           //内容
    QHostAddress cliAddr;           //对方地址
    quint16 port;                   //对方端口
    qint64 len = udpSocket->readDatagram(buf,sizeof(buf),&cliAddr,&port);
    //添加时间
    QTime cur_time = QTime::currentTime();
    QString time_info = cur_time.toString("hh:mm:ss");
    if(len >0)
    {
        //获取到文本格式化 如 [192.168.1.1 : 8888]aaaa 的格式
        QString str = QString("[%1:%2] %3 [%4]")
                .arg(cliAddr.toString())
                .arg(port)
                .arg(buf)
                .arg(time_info);
        //ui->textEditShow->setText(str);
        ui->textEditShow->append(str);
    }

}

然后便是对事件过滤器的函数实现

bool Widget::eventFilter(QObject *target, QEvent *event)
{
    if(target == ui->textEditShow) //对象为需要发送的焦点
        {
            if(event->type() == QEvent::KeyPress)//回车键
            {
                 QKeyEvent *k = static_cast<QKeyEvent *>(event);

                 if(k->key() == Qt::Key_Return)
                 {
                     on_ButtonSend_clicked();
                     return true;
                 }
            }
        }
        return QWidget::eventFilter(target,event);
}

最后是点击发送按钮,获取编辑框中的内容,使用通信套接字,将其发送给绑定特定端口号的一方

void Widget::on_ButtonSend_clicked()
{
    //获取对方的ip和端口
    QString ip = ui->lineEditIp->text();
    qint16 port = ui->lineEditPort->text().toInt();

    //获取编辑区内容
    QString str = ui->textEditShow->toPlainText();
    //给指定的ip发送数据
    udpSocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port);
    ui->textEditShow->clear();

}

c.secondwidget.h

添加socket头文件方法和声明,以及消息队列处理函数,和键盘响应函数同上

public:
	void dealMsg2();         //槽函数,处理对方发过来的数据
protected:
    bool eventFilter(QObject *target, QEvent *event);//事件过滤器
private:
    QUdpSocket *udpSocket;

d.secondwidget.cpp

构造函数和第一个界面有一点点不同,如下

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

    this->resize(450,300);
    this->setWindowTitle("服务器端口2:9999");
    udpSocket2 = new QUdpSocket(this);
    //绑定
    udpSocket2->bind(9999);
    connect(udpSocket2,&QUdpSocket::readyRead,this,&SecondWidget::dealMsg2);
    ui->ButtonSend2->setFocus();
    ui->ButtonSend2->setDefault(true);
    //ui->ButtonSend->setShortcut(Qt::Key_Enter|Qt::Key_Return);
    ui->textEditShow2->installEventFilter(this);//设置完后自动调用其eventFilter函数
}

其他的函数实现方式和第一个界面相同,这类不再赘述。待正确编译之后,即可实现这样的效果(????????)

使用Qt的UDP通信协议,实现局域网通信功能(仿组播软件版本--简单)

  • 注意:Qt中UDP通信只需要在同一个ip下(需要绑定自己电脑上的ip,使用ipconfig命令查看该电脑的ipv4的地址),绑定对应的端口号。即可进行通信连接

3.源代码

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QUdpSocket>

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
    Q_OBJECT

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

    void dealMsg();         //槽函数,处理对方发过来的数据
protected:
    bool eventFilter(QObject *target, QEvent *event);//事件过滤器

private slots:
    void on_ButtonSend_clicked();

    void on_ButtonClose_clicked();

private:
    Ui::Widget *ui;

private:
    QUdpSocket *udpSocket;
};

#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
#include <QDebug>
#include <QTime>
#include <QKeyEvent>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->resize(450,300);
    this->setWindowTitle("服务器端口:8888");
    udpSocket = new QUdpSocket(this);

    //绑定端口号
    udpSocket->bind(8888);
    //udpSocket->bind(QHostAddress::AnyIPv4,8888);        //必须是主机的ipv4类通信方式,以及本机的ip地址
        
    //当对方发送数据过来。自动触发readyRead()
    connect(udpSocket,&QUdpSocket::readyRead,this,&Widget::dealMsg);

    ui->ButtonSend->setFocus();
    ui->ButtonSend->setDefault(true);
    ui->textEditShow->installEventFilter(this);//设置完后自动调用其eventFilter函数

}

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

void Widget::dealMsg()
{
    //读取对方发送的内容
    char buf[1024] = {0};           //内容
    QHostAddress cliAddr;           //对方地址
    quint16 port;                   //对方端口
    qint64 len = udpSocket->readDatagram(buf,sizeof(buf),&cliAddr,&port);
    QTime cur_time = QTime::currentTime();
    QString time_info = cur_time.toString("hh:mm:ss");
    if(len >0)
    {
        //获取到文本格式化  [192.168.1.1 : 8888]aaaa
        QString str = QString("[%1:%2] %3 [%4]")
                .arg(cliAddr.toString())
                .arg(port)
                .arg(buf)
                .arg(time_info);
        //ui->textEditShow->setText(str);
        ui->textEditShow->append(str);
    }

}

bool Widget::eventFilter(QObject *target, QEvent *event)
{
    if(target == ui->textEditShow)
        {
            if(event->type() == QEvent::KeyPress)//回车键
            {
                 QKeyEvent *k = static_cast<QKeyEvent *>(event);

                 if(k->key() == Qt::Key_Return)
                 {
                     on_ButtonSend_clicked();
                     return true;
                 }
            }
        }
        return QWidget::eventFilter(target,event);
}

void Widget::on_ButtonSend_clicked()
{
    //获取对方的ip和端口
    QString ip = ui->lineEditIp->text();
    qint16 port = ui->lineEditPort->text().toInt();

    //获取编辑区内容
    QString str = ui->textEditShow->toPlainText();
    //给指定的ip发送数据
    udpSocket->writeDatagram(str.toUtf8(),QHostAddress(ip),port);
    ui->textEditShow->clear();

}

void Widget::on_ButtonClose_clicked()
{
    udpSocket->disconnectFromHost();
    udpSocket->close();
    qDebug() << "主服务器连接断开!";
    this->close();

}

secondwidget.h

#ifndef SECONDWIDGET_H
#define SECONDWIDGET_H

#include <QWidget>
#include <QUdpSocket>

namespace Ui {
class SecondWidget;
}

class SecondWidget : public QWidget
{
    Q_OBJECT

public:
    explicit SecondWidget(QWidget *parent = nullptr);
    ~SecondWidget();
    void dealMsg2();            //消息队列处理
protected:
    bool eventFilter(QObject *target, QEvent *event);//事件过滤器

private slots:
    void on_ButtonSend2_clicked();

    void on_ButtonClose2_clicked();

private:
    Ui::SecondWidget *ui;
private:
    QUdpSocket *udpSocket2;

};

#endif // SECONDWIDGET_H

secondwidget.cpp

#include "secondwidget.h"
#include "ui_secondwidget.h"
#include <QHostAddress>
#include <QDebug>
#include <QTime>
#include <QKeyEvent>

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

    this->resize(450,300);
    this->setWindowTitle("服务器端口2:9999");
    udpSocket2 = new QUdpSocket(this);
    //绑定
    udpSocket2->bind(9999);
    connect(udpSocket2,&QUdpSocket::readyRead,this,&SecondWidget::dealMsg2);
    ui->ButtonSend2->setFocus();
    ui->ButtonSend2->setDefault(true);
    //ui->ButtonSend->setShortcut(Qt::Key_Enter|Qt::Key_Return);
    ui->textEditShow2->installEventFilter(this);//设置完后自动调用其eventFilter函数
}

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

void SecondWidget::dealMsg2()
{
    //读取对方发送的内容
    char buf[1024] = {0};           //内容
    QHostAddress cliAddr;           //对方地址
    quint16 port;                   //对方端口
    qint64 len = udpSocket2->readDatagram(buf,sizeof(buf),&cliAddr,&port);
    QTime cur_time = QTime::currentTime();
    QString time_info = cur_time.toString("hh:mm:ss");
    if(len >0)
    {
        //获取到文本格式化  [192.68.1.144 : 8888]aaaa
        QString str = QString("[%1:%2] %3 [%4]")
                .arg(cliAddr.toString())
                .arg(port)
                .arg(buf)
                .arg(time_info);
        //ui->textEditShow2->setText(str);
        ui->textEditShow2->append(str);
    }
}

bool SecondWidget::eventFilter(QObject *target, QEvent *event)
{
    if(target == ui->textEditShow2)
        {
            if(event->type() == QEvent::KeyPress)//回车键
            {
                 QKeyEvent *k = static_cast<QKeyEvent *>(event);

                 if(k->key() == Qt::Key_Return)
                 {
                     on_ButtonSend2_clicked();
                     return true;
                 }
            }
        }
        return QWidget::eventFilter(target,event);
}

void SecondWidget::on_ButtonSend2_clicked()
{
    //获取对方的ip和端口
    QString ip = ui->lineEditIp2->text();
    qint16 port = ui->lineEditPort2->text().toInt();

    //获取编辑区内容
    QString str = ui->textEditShow2->toPlainText();
    //给指定的ip发送数据
    udpSocket2->writeDatagram(str.toUtf8(),QHostAddress(ip),port);
    ui->textEditShow2->clear();
}

void SecondWidget::on_ButtonClose2_clicked()
{
    udpSocket2->disconnectFromHost();
    udpSocket2->close();
    qDebug() << "断开连接!";
    this->close();


}

如果感兴趣想要源文件的,请移至下载(????????????)源代码文件下载

相关标签: QT qt5 tcpip