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

一、UDP通信概述

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

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

UDP在Qt中的应用探索与研究_第1张图片

                                                                        图 1-1 

二、UDP的分类

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

UDP在Qt中的应用探索与研究_第2张图片

                                                                图 2-1 

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

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

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

三、UDP单播与广播

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

效果图:

UDP在Qt中的应用探索与研究_第3张图片

                                                                图 3-1

UDP在Qt中的应用探索与研究_第4张图片

图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为本地管理组播地址,仅在特定的本地范围内有效;

效果图:

UDP在Qt中的应用探索与研究_第5张图片

图 4-1

UDP在Qt中的应用探索与研究_第6张图片

图 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;
}
 

你可能感兴趣的:(Qt,C++,Socket,udp,网络协议,网络)