• UDP在Qt中的应用探索与研究


    一、UDP通信概述

            UDP(User Datagram Protocol,用户数据报协议)是轻量的、不可靠的、面向数据报、无连接的协议,它可以用于对可靠性要求不高的场合。与TCP通信不同,两个程序之间进行UDP通信无需预先建立持久的socket连接,UDP每次发送数据报都需要制定目标地址和端口。

            QUdpSocket类用于实现UDP通信,它从QAbstractSocket类继承,因而与QTcpSocket共享大部分接口函数。主要区别是QUdpSocket以数据报传输数据,而不是以连续的数据流。发送数据报使用函数QUdpSocket::writeDatagram(),数据报的长度一般少于512字节,每个数据报包含发送者和接收者的IP地址和端口等信息。

                                                                            图 1-1 

    二、UDP的分类

    UDP消息传送有单播、广播、组播三种模式。 

                                                                    图 2-1 

    单播模式:一个UDP客户端发出的数据报只发送到另一个制定地址和端口的UDP客户端,是一对一的数据传输。

    广播模式:一个UDP客户端发出的数据报,在同一网络范围内其他所有的UDP客户端都可以收到。支持IPV4广播,是实现网络发现的协议。要获取广播数据只需要在数据报中制定接收端地址为QHostAddress::Broadcast,广播地址是:255.255.255.255。

    组播模式:也称多播。UDP客户端加入到另一个组播IP地址制定的多播组,成员向组播地址发送的数据报组内成员都可以接收到,类似于QQ群的功能。QUdpSocket::joinMulticastGroup()函数实现加入组播的功能,加入组播后可以正常进行数据收发,UDP通信实现比较灵活,TCP通信只有单播模式,没有广播与组播模式。UDP通信不能保证数据的准确性,但具有灵活性,可用于即时通信。

    三、UDP单播与广播

            UDP单播主要是建立在不同端上的一对一非连接的通信,时效高,具有较好的灵活性;UDP广播主要是建立在不同端上的一对多的非连接的通信。

    效果图:

                                                                    图 3-1

    图3-2

     代码示例:

    mainwindow.h:

    class MainWindow : public QMainWindow
    {
        Q_OBJECT

    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
        void initWindow();
    private slots:
        void onSockStateChange(QAbstractSocket::SocketState socketState);
        void onSockReadyRead();
        void on_actStart_clicked();

        void on_actStop_clicked();

        void on_clearBtn_clicked();

        void on_exitBtn_clicked();

        void on_btnSend_clicked();

        void on_btnBroadcast_clicked();

    private:
        QString getLocalIP();
    private:
        Ui::MainWindow *ui;
        QLabel  *_pLabSocketState;
        QUdpSocket  *_pUdpSocket;
    };

    #endif // MAINWINDOW_H

    mainwindow.cpp:

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H

    #include
    #include
    #include

    namespace Ui {
    class MainWindow;
    }

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

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        initWindow();
    }

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

    void MainWindow::initWindow()
    {
        _pLabSocketState = new QLabel(QString::fromLocal8Bit("Socket状态: "));
        _pLabSocketState->setMinimumWidth(200);
        ui->statusBar->addWidget(_pLabSocketState);

        QString localIP = getLocalIP();
        QString sTitle = this->windowTitle() + QString::fromLocal8Bit("----本机IP:") + localIP;
        this->setWindowTitle(sTitle);
        ui->comboTargetIP->addItem(localIP);
        ui->bindSpinBox->setRange(0, 8999);
        ui->bindSpinBox->setValue(1200);
        ui->targetSpinBox->setRange(0, 8999);
        ui->targetSpinBox->setValue(3355);

        _pUdpSocket = new QUdpSocket(this);
        connect(_pUdpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSockStateChange(QAbstractSocket::SocketState)));
        connect(_pUdpSocket, SIGNAL(readyRead()), this, SLOT(onSockReadyRead()));
    }

    void MainWindow::onSockStateChange(QAbstractSocket::SocketState socketState)
    {
        switch (socketState)
        {
        case QAbstractSocket::UnconnectedState:
            _pLabSocketState->setText(QString::fromLocal8Bit("socket 状态: UnconnectedState"));
            break;
        case QAbstractSocket::HostLookupState:
            _pLabSocketState->setText(QString::fromLocal8Bit("socket 状态: HostLookupState"));
            break;
        case QAbstractSocket::ConnectingState:
            _pLabSocketState->setText(QString::fromLocal8Bit("socket 状态: ConnectingState"));
            break;
        case QAbstractSocket::ConnectedState:
            _pLabSocketState->setText(QString::fromLocal8Bit("socket 状态: ConnectedState"));
            break;
        case QAbstractSocket::BoundState:
            _pLabSocketState->setText(QString::fromLocal8Bit("socket 状态: BoundState"));
            break;
        case QAbstractSocket::ClosingState:
            _pLabSocketState->setText(QString::fromLocal8Bit("socket 状态: ClosingState"));
            break;
        case QAbstractSocket::ListeningState:
            _pLabSocketState->setText(QString::fromLocal8Bit("socket 状态: ListeningState"));
            break;
        default:
            break;
        }
    }

    void MainWindow::onSockReadyRead()
    {
        //读取收到的数据报
        while(_pUdpSocket->hasPendingDatagrams())
        {
            QByteArray datagram;
            datagram.resize(_pUdpSocket->pendingDatagramSize());
            QHostAddress peerAddr;
            quint16 peerPort;
            _pUdpSocket->readDatagram(datagram.data(), datagram.size(), &peerAddr, &peerPort);
            QString str = datagram.data();
            QString peer = "[From "+ peerAddr.toString() +":" + QString::number(peerPort) +"] ";
            ui->plainTextEdit->appendPlainText(peer + str);
        }
    }

    QString MainWindow::getLocalIP()
    {
        QString hostName = QHostInfo::localHostName(); //主机名
        QHostInfo hostInfo = QHostInfo::fromName(hostName);
        QString localIP = "";
        QList addList = hostInfo.addresses();

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

    void MainWindow::on_actStart_clicked()
    {
        quint16 port = ui->bindSpinBox->value();
        if(_pUdpSocket->bind(port))
        {
            ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**已成功绑定"));
            QString sMsg = QString::fromLocal8Bit("**绑定端口:") + QString::number(_pUdpSocket->localPort());
            ui->plainTextEdit->appendPlainText(sMsg);
            ui->actStart->setEnabled(false);
            ui->actStop->setEnabled(true);
        }
        else
        {
            ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**绑定失败"));
        }
    }

    void MainWindow::on_actStop_clicked()
    {
        _pUdpSocket->abort();
        ui->actStart->setEnabled(true);
        ui->actStop->setEnabled(false);
        ui->plainTextEdit->appendPlainText(QString::fromLocal8Bit("**已解除绑定"));
    }

    void MainWindow::on_clearBtn_clicked()
    {
        ui->editMsg->clear();
        ui->plainTextEdit->clear();
    }

    void MainWindow::on_exitBtn_clicked()
    {
       _pUdpSocket->abort();
       this->close();
    }

    void MainWindow::on_btnSend_clicked() //单播
    {
        //发送
        QString targetIP = ui->comboTargetIP->currentText();
        QHostAddress targetAddr(targetIP);
        quint16 targetPort = ui->targetSpinBox->value();
        QString msg = ui->editMsg->text();
        QByteArray str = msg.toUtf8();
        _pUdpSocket->writeDatagram(str, targetAddr, targetPort);
        ui->plainTextEdit->appendPlainText("[out] " + msg);
        ui->editMsg->clear();
        ui->editMsg->setFocus();
    }

    void MainWindow::on_btnBroadcast_clicked() //广播
    {
        //广播
        quint16 targetPort = ui->targetSpinBox->value();
        QString msg = ui->editMsg->text();
        QByteArray str = msg.toUtf8();
        _pUdpSocket->writeDatagram(str, QHostAddress::Broadcast, targetPort);
        ui->plainTextEdit->appendPlainText("[broadcast] " + msg);
        ui->editMsg->clear();
        ui->editMsg->setFocus();
    }
    四、Udp组播

    Udp组播是主机之间“一对一组”的通信模式,当多个客户端加入由一个组播地址定义的多播组之后,客户端向组播地址和端口发送的UDP数据报,组内成员都可以接收到。组播报文的目的地址使用D类IP地址,D类地址不能出现在IP报文的源IP地址字段。

    说明:关于组播IP地址,有如下约定:

    1)224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留不做分配,其他地址供路由协议使用;

    2)224.0.1.0~238.255.255.255是公用组播地址,可以用于Internet;

    3)224.0.2.0~238.255.255.255为用户可用组播地址(临时组播地址),全网范围内有效;

    4)239.0.0.0~239.255.255.255为本地管理组播地址,仅在特定的本地范围内有效;

    效果图:

    图 4-1

    图 4-2 

     代码示例:

    mainwindow.h:

    #ifndef MAINWINDOW_H
    #define MAINWINDOW_H

    #include
    #include
    #include

    namespace Ui {
    class MainWindow;
    }

    class MainWindow : public QMainWindow
    {
        Q_OBJECT

    public:
        explicit MainWindow(QWidget *parent = 0);
        ~MainWindow();
    private slots:
        void onSocketStateChange(QAbstractSocket::SocketState socketState);
        void onSocketReadyRead();//读取socket传入的数据
        void on_bindBtn_clicked();
        void on_stopBtn_clicked();
        void on_btnSend_clicked();
        void on_clearBtn_clicked();
        void on_quitBtn_clicked();
    private:
        QLabel* LabSocketState;//socket状态显示标签
        QUdpSocket* udpSocket;
        QString getLocalIp();//获取本机IP地址
        QHostAddress groupAddress;//组播地址

    private:
        Ui::MainWindow *ui;
    };

    #endif // MAINWINDOW_H


    mainwindow.cpp:

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

    MainWindow::MainWindow(QWidget *parent) :
        QMainWindow(parent),
        ui(new Ui::MainWindow)
    {
        ui->setupUi(this);
        ui->comboTargetIp->addItem("224.0.0.1");
        ui->comboTargetIp->addItem("239.255.43.21");
        ui->comboTargetIp->setCurrentIndex(0);

        LabSocketState = new QLabel(QString::fromLocal8Bit("Socket 状态"));
        LabSocketState->setMinimumWidth(200);
        ui->statusBar->addWidget(LabSocketState);
        QString localIP = getLocalIp();//获取IP地址
        this->setWindowTitle(this->windowTitle()+QString::fromLocal8Bit("---本机IP")+localIP);
        udpSocket = new QUdpSocket;
        udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption,1);//对socket进行参数设置
        connect(udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),this,SLOT(onSocketStateChange(QAbstractSocket::SocketState)));
        onSocketStateChange(udpSocket->state());
        connect(udpSocket,SIGNAL(readyRead()),this,SLOT(onSocketReadyRead()));

    }

    void MainWindow::on_bindBtn_clicked()
    {//加入组播
        QString IP = ui->comboTargetIp->currentText();
        groupAddress = QHostAddress(IP);
        quint16 groupPort = ui->spinBox->value();//端口
        if(udpSocket->bind(QHostAddress::AnyIPv4,groupPort,QUdpSocket::ShareAddress))
        {
            udpSocket->joinMulticastGroup(groupAddress);//加入多播组
            ui->plainTextEdit->append(QString::fromLocal8Bit("**加入组播成功"));
            ui->plainTextEdit->append(QString::fromLocal8Bit("组播地址IP: ")+IP);
            ui->plainTextEdit->append(QString::fromLocal8Bit("**绑定端口:") + QString::number(groupPort));
            ui->bindBtn->setEnabled(false);
            ui->stopBtn->setEnabled(true);
            ui->comboTargetIp->setEnabled(false);
        }
        else
        {
            ui->plainTextEdit->append(QString::fromLocal8Bit("**绑定端口失败"));
        }

    }

    void MainWindow::on_stopBtn_clicked()
    {//退出组播
        udpSocket->leaveMulticastGroup(groupAddress);//退出组播
        udpSocket->abort();
        ui->bindBtn->setEnabled(true);
        ui->stopBtn->setEnabled(false);
        ui->comboTargetIp->setEnabled(true);
        ui->plainTextEdit->append(QString::fromLocal8Bit("**已退出组播,解除端口绑定"));
    }

    void MainWindow::on_btnSend_clicked()
    {   //发送组播消息
        QString msg = ui->lineEdit->text();
        QByteArray array;
        array = msg.toUtf8();
        udpSocket->writeDatagram(array,groupAddress,groupPort);
        ui->plainTextEdit->append("[multicst]"+msg);
        ui->lineEdit->clear();
        ui->lineEdit->setFocus();
    }

    void MainWindow::on_clearBtn_clicked()
    {
        ui->plainTextEdit->clear();
    }

    void MainWindow::on_quitBtn_clicked()
    {
        this->close();
    }

    void MainWindow::onSocketReadyRead()
    {//读取数据报
        QByteArray datagram;
        datagram.resize(udpSocket->pendingDatagramSize());
        QHostAddress peerAddr;
        quint16 peerPort;
        udpSocket->readDatagram(datagram.data(),datagram.size(),&peerAddr,&peerPort);
        QString str =datagram.data();
        QString peerStr = "[From"+peerAddr.toString()+":"+QString::number(peerPort)+"]";
        ui->plainTextEdit->append(peerStr + str);   
    }
    QString MainWindow::getLocalIp()
    {//获取本机IP
        QString hostName = QHostInfo::localHostName();//本机主机名
        QHostInfo hostInfo = QHostInfo::fromName(hostName);
        QString localIP = " ";
        QList addList = hostInfo.addresses();
        if(!addList.isEmpty())
        {
            for(int i=0;i         {
                QHostAddress aHost = addList.at(i);
                if(QAbstractSocket::IPv4Protocol == aHost.protocol())
                {
                    localIP = aHost.toString();
                    break;
                }
            }
        }
        return localIP;
    }

    void MainWindow::onSocketStateChange(QAbstractSocket::SocketState socketState)
    {
        //socket状态变化
        switch(socketState)
        {
            case QAbstractSocket::UnconnectedState:
            {
                LabSocketState->setText(QString::fromLocal8Bit("socket状态:UnconnectedState"));
                break;
            }
            case QAbstractSocket::HostLookupState:
            {
                LabSocketState->setText(QString::fromLocal8Bit("socket状态:HostLookupState"));
                break;
            }
            case QAbstractSocket::ConnectingState:
            {
                LabSocketState->setText(QString::fromLocal8Bit("socket状态:ConnectingState"));
                break;
            }
            case QAbstractSocket::ConnectedState:
            {
                LabSocketState->setText(QString::fromLocal8Bit("socket状态:ConnectedState"));
                break;
            }
            case QAbstractSocket::BoundState:
            {
                LabSocketState->setText(QString::fromLocal8Bit("socket状态:BoundState"));
                break;
            }
            case QAbstractSocket::ClosingState:
            {
                LabSocketState->setText(QString::fromLocal8Bit("socket状态:ClosingState"));
                break;
            }
            case QAbstractSocket::ListeningState:
            {
                LabSocketState->setText(QString::fromLocal8Bit("socket状态:ListeningState"));
                break;
            }
        }

    }

    MainWindow::~MainWindow()
    {
        delete ui;
        udpSocket = NULL;
        delete udpSocket;
    }
     

  • 相关阅读:
    学校介绍静态HTML网页设计作品 DIV布局学校官网模板代码 DW大学网站制作成品下载 HTML5期末大作业
    两字符串拼接形成回文串
    2023/11/10 JAVA学习
    01-数据结构和算法入门
    Python读取PDF文字 去掉页眉页脚
    Visual Studio 中使用 CMake
    Facebook内容的类型
    你也可以很硬核「GitHub 热点速览 v.22.13」
    Go实现udp服务
    注解的应用:模拟Junit框架
  • 原文地址:https://blog.csdn.net/leiyang2014/article/details/125951124