Bootstrap

Qt 网络编程 udp通信

学习目标:使用udp通信

前置环境

运行环境:qt creator 4.12

学习内容

UDP 协议基础知识
1、UDP(用户数据报协议)是轻量的、不可靠的、面向数据报、无连接的协议,用于可靠性要求不高的场合。两个应用程序之间进行UDP 通信不需先建立持久的 socket 连接,UDP 每次发送数据报都需要指定目标地址和端口。

2.ÙDP 报文没有可靠性保证、顺序保证和流量控制字段等,可靠性一较差。但是正因为 UDP 协议的控制选项较少在数据传输过程中延迟小、数据传输效率高,适合对可靠性要求不高的应用程序,或者可以保障可靠性的应用程序,如 DNS、TFTP、SNMP 等。

3、UDP 报头由 4个域组成,其中每个域各占用 2 个字节,具体包括
源端口号、目标端口号、数据包长度、校验值。端口号有效范围0--65535,假设端口号大于 49151的端口都代表动态端口

4、QUdpSocket类从QAbstractSocket类继承 基本跟QTcpSocket共用大部分的接口函数,主要区别在于 QUdpSocket 以数据报传输数据,不是以连续的数据流,发送方发送数据报使用函数QUdpSocket::writeDataGram(),数据报长度一般不超过 512 个字节,每个数据报包含发送方和接收方的 IP 地址和端口等数据信息。

QUdpSocket是Qt中用于UDP网络通信的类,它提供了以下一些常用的成员函数:

  1. bind(const QHostAddress &address, quint16 port): 绑定到指定的地址和端口。

  2. bind(quint16 port, BindMode mode = DefaultForPlatform): 绑定到指定的端口,并设置绑定模式。

  3. writeDatagram(const QByteArray &datagram, const QHostAddress &host, quint16 port): 将指定的数据报发送到指定的主机和端口。

  4. writeDatagram(const char *data, qint64 size, const QHostAddress &host, quint16 port): 将指定大小的数据发送到指定的主机和端口。

  5. readDatagram(char *data, qint64 maxSize, QHostAddress *address = nullptr, quint16 *port = nullptr): 从接收缓冲区中读取一个数据报,并将其存储在指定的缓冲区中。

  6. pendingDatagramSize(): 返回下一个待读取的数据报的大小。

  7. hasPendingDatagrams(): 检查是否有待读取的数据报。

  8. joinMulticastGroup(const QHostAddress &groupAddress): 加入一个多播组。

  9. leaveMulticastGroup(const QHostAddress &groupAddress): 退出一个多播组。

  10. setMulticastInterface(const QNetworkInterface &interface): 设置用于多播的网络接口。

  11. multicastInterface(): 返回用于多播的网络接口。

  12. setSocketOption(SocketOption option, const QVariant &value): 设置套接字选项。

  13. socketOption(SocketOption option): 返回指定的套接字选项的值。

  14. state(): 返回套接字的当前状态。

  15. error(): 返回最近一次发生的错误。

  16. abort() 用于中断或终止当前的 UDP 网络操作。可以确保 QUdpSocket 实例回到一个干净、可重用的状态,而不会产生任何残留的网络操作。需要注意的是,abort() 函数只会中断当前正在进行的操作,而不会关闭套接字本身。如果需要彻底关闭套接字,可以调用 close() 函数。

    1. 立即中断正在进行的任何 UDP 发送或接收操作。

    2. 清空内部缓冲区,丢弃所有待发送或待接收的数据。

    3. 将套接字的状态设置为 QAbstractSocket::UnconnectedState

这些成员函数涵盖了UDP网络编程的各个方面,包括数据发送、数据接收、多播管理、套接字选项设置等。使用这些函数可以方便地实现UDP通信的各种功能。

实现项目

Qt udp通讯编程

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    //获取当前主机ip  放到ip选项栏
    QHostInfo info =QHostInfo::fromName(QHostInfo::localHostName());
    QList<QHostAddress> addrs=info.addresses();
    if(!addrs.empty()){
        foreach(const QHostAddress& addr,addrs){
            if(addr.protocol()==QAbstractSocket::IPv4Protocol){
                ui->plainTextEdit->appendPlainText("本机当前ipv4:"+addr.toString());
                ui->tarip->addItem(addr.toString());
            }
        }
    }


    udpsocket=new QUdpSocket(this);

    ui->pushButton->setEnabled(true);
    ui->pushButton_3->setEnabled(false);

    //用作消息到达 处理消息
    // QObject::connect(udpsocket,SIGNAL(readyRead()),this,SLOT(UdpSockerRecvData())); 老版本Qt4.0语法糖 5.0弃用但是仍然可使用

    //QObject::connect(udpsocket,SIGNAL(QUdpSocket::readyRead()),this,SLOT(MainWindow::UdpSockerRecvData())); 新版本5.0 误区点
    //新版本5.0
    QObject:: connect(udpsocket, &QUdpSocket::readyRead, this, &MainWindow::UdpSockerRecvData);
}
void MainWindow::UdpSockerRecvData(){
    while(udpsocket->hasPendingDatagrams())
       {
           QByteArray datagrams;

           datagrams.resize(udpsocket->pendingDatagramSize());

           QHostAddress paddress;
           quint16 pport;

           // 通过readDatagram()此函数读取数据报,
           udpsocket->readDatagram(datagrams.data(),datagrams.size(),&paddress,&pport);

           QString strs=datagrams.data();
           QString peer="[From:"+paddress.toString()+":"+QString::number(pport)+"]:";

           ui->plainTextEdit->appendPlainText(peer+strs);

           qDebug()<<"UdpSockerRecvData:"<<peer;
       }
}

MainWindow::~MainWindow()
{
    delete ui;
    udpsocket->close();
    delete udpsocket;
}


void MainWindow::on_pushButton_clicked()//绑定端口
{
    QString ip=ui->tarip->currentText();
    qint16 port =ui->curport->value();

    if(udpsocket->bind(QHostAddress(ip),port)){
        ui->plainTextEdit->appendPlainText("**********绑定成功**********");
        ui->plainTextEdit->appendPlainText("$$$$$$$$$$绑定端口$$$$$$$$$$:"+QString::number(udpsocket->localPort()));
        ui->pushButton->setEnabled(false);
        ui->pushButton_3->setEnabled(true);
        qDebug()<<"绑定端口:"<<port;

    }else ui->plainTextEdit->appendPlainText("**********绑定失败**********");
}

void MainWindow::on_pushButton_3_clicked()//解除绑定端口
{
    qDebug()<<"解除绑定端口:"<<udpsocket->localPort();
    udpsocket->abort();
    ui->pushButton->setEnabled(true);
    ui->pushButton_3->setEnabled(false);
    ui->plainTextEdit->appendPlainText("**********已经停止服务**********");
}

void MainWindow::on_pushButton_2_clicked()//发送数据
{
     // 获取目标IP地址 端口 发送消息内容
    QString targetip=ui->tarip->currentText();
    uint16_t targetport =ui->tarport->value();
    QString msg =ui->editmsg->text();
    qDebug()<<"发送数据:"<<targetip<<targetport;
    QByteArray send =msg.toUtf8();
    // 发送数据报信息
    udpsocket->writeDatagram(send,QHostAddress(targetip),targetport);
    ui->plainTextEdit->appendPlainText("[out]:"+msg);
    ui->editmsg->clear();
}

void MainWindow::on_pushButton_4_clicked()//广播发送
{
    qint16 tarport =ui->tarport->value();
    QString msg =ui->editmsg->text();
    QByteArray send =msg.toUtf8();

    udpsocket->writeDatagram(send,QHostAddress::Broadcast,tarport);
    ui->plainTextEdit->appendPlainText("[broadcast out]:"+msg);
    ui->editmsg->clear();

    ui->editmsg->setFocus(); // 将光标焦点定位到编辑框控件
}

 总结

  1. 获取本地IP地址:

    • 通过 QHostInfo::fromName(QHostInfo::localHostName()) 获取本地主机信息
    • 遍历 QHostInfo::addresses() 中的地址,找到 IPv4 协议的地址
    • 将这些地址添加到 tarip 下拉框中供用户选择
  2. 创建 UDP 套接字:

    • 使用 QUdpSocket 类创建 UDP 套接字实例
    • 将 readyRead 信号连接到 UdpSockerRecvData 槽函数,用于处理接收到的数据
  3. 绑定 UDP 端口:

    • 在 on_pushButton_clicked 函数中,获取用户选择的 IP 地址和端口
    • 使用 udpsocket->bind(QHostAddress(ip), port) 绑定 UDP 套接字
    • 根据绑定结果更新 UI 状态和按钮可用性
  4. 接收 UDP 数据报:

    • 在 UdpSockerRecvData 槽函数中,通过 udpsocket->hasPendingDatagrams() 检查是否有待处理的数据报
    • 使用 udpsocket->readDatagram() 读取数据报,获取发送方地址和端口
    • 将接收到的消息显示在 plainTextEdit 窗口中
  5. 发送 UDP 数据报:

    • 在 on_pushButton_2_clicked 函数中,获取用户输入的目标 IP 地址、端口和消息
    • 使用 udpsocket->writeDatagram() 将消息发送到指定的目标
    • 在 plainTextEdit 窗口中显示发送的消息
  6. 广播 UDP 数据报:

    • 在 on_pushButton_4_clicked 函数中,获取用户输入的目标端口和消息
    • 使用 udpsocket->writeDatagram(data, QHostAddress::Broadcast, port) 将消息广播到所有客户端
    • 在 plainTextEdit 窗口中显示广播的消息
  7. 资源清理:

    • 在 MainWindow 析构函数中,关闭 UDP 套接字并删除实例
    • 确保资源得到正确释放,避免内存泄漏
最后附上源代码链接
对您有帮助的话,帮忙点个star

34-udpSocket · jbjnb/Qt demo - 码云 - 开源中国 (gitee.com)

;