10.4 基于UDP的广播程序

10.4 基于UDP的广播程序

  UDP(User Data Protocol)是一种基于数据报的传输层的协议,它是一种面向无连接,且不负责数据可靠性的网络传输层协议。它常常被用在一些对速度要求比较快的场合中,比如直播,短消息等场合中。它适用于以下的场景:

  (1)拥有大量客户端

  (2)对速度要求高

  (3)传送的内容可以忍受信息的丢失

  (4)面向无连接

10.4.1 UDP协议的工作原理

  UDP的首先要知道它不需要面向连接,在最简单的双向通信的例子中,客户端和服务器端之间只需要有各自所绑定的端口即可,而IP地址只要是位于同一子网就行。而UDP协议本身也规定了最大的报文大小一般是64kb。由于UDP协议不存在超时重传机制,因此服务器端未收到请求时,客户端不会进行重发。因此报文的传输是不可靠的。客户端和服务端遵循着:请求-应答-请求-应答-请求应答-....这样的流程来完成数据的传输。

10.4.2 UDP编程模型

  对于UDP的编程模型,具体分为了客户端和服务端两个部分。那么服务端的话,一般的步骤是:初始化套接字->绑定服务端的端口号->接收数据。而客户端的话,就是:初始化套接字->绑定端口(可有可无)->发送数据到服务端对应的端口上。

10.4.3 UDP服务器的编程实例

  案例(一)编写一个简单的UDP服务端的广播程序

  (1)新建QT GUI应用程序,类名为UDPServer,基类选择QWidget。

  (2)在udpserver.h中编写以下成员

#ifndef UDPSERVER_H
#define UDPSERVER_H
#include <QList>
#include <QLabel>
#include <QDebug>
#include <QTimer>
#include <QWidget>
#include <QLineEdit>
#include <QMessageBox>
#include <QUdpSocket>
#include <QPushButton>
#include <QVBoxLayout>
class UDPServer : public QWidget
{
    Q_OBJECT
public:
    UDPServer(QWidget *parent = nullptr);
    ~UDPServer();
private slots:
    void onTimeOut();
    void onClicked();
private:
    QLabel*m_timerLabel;
    QLineEdit*m_timeLineEdit;
    QPushButton*m_startPushButton;
private:
    QUdpSocket*m_serverSocket;
private:
    QVBoxLayout*m_mainLayout;
private:
    QTimer*m_timer;
};
#endif // UDPSERVER_H

  (3)在udpserver.cpp中添加这些实现

#include "udpserver.h"

UDPServer::UDPServer(QWidget *parent):QWidget(parent)
{
    m_mainLayout = new QVBoxLayout(this);
    m_timerLabel = new QLabel("计时器:");
    m_timeLineEdit = new QLineEdit;
    m_startPushButton = new QPushButton("开始");
    m_timer = new QTimer(this);

    m_mainLayout->addWidget(m_timerLabel);
    m_mainLayout->addWidget(m_timeLineEdit);
    m_mainLayout->addWidget(m_startPushButton);

    m_serverSocket = new QUdpSocket(this);
    m_serverSocket->bind(8888);//我们为Server绑定一个端口
    connect(this->m_startPushButton,&QPushButton::clicked,this,&UDPServer::onClicked);
    connect(this->m_timer,&QTimer::timeout,this,&UDPServer::onTimeOut);
}

UDPServer::~UDPServer()
{
    delete m_timerLabel;
    m_timerLabel = nullptr;
    delete m_timeLineEdit;
    m_timeLineEdit = nullptr;
    delete m_startPushButton;
    m_startPushButton = nullptr;
    delete m_mainLayout;
    m_mainLayout = nullptr;
    delete m_serverSocket;
    m_serverSocket = nullptr;
    delete m_timer;
    m_timer = nullptr;
}

void UDPServer::onTimeOut()
{
    char data[] = "Hello,World!\n";
    this->m_serverSocket->writeDatagram(data,sizeof(data),QHostAddress(QHostAddress::Broadcast),9999);//我们知道Client的端口是9999,那么直接发送即可
}

void UDPServer::onClicked()
{
    if(this->m_timeLineEdit->text().isEmpty())
    {
        QMessageBox::warning(this,QString("提示"),QString("未输入任何的时长!"));
    }
    else
    {
        if(m_startPushButton->text() == "开始")
        {
            bool isOk(false);
            int ms = this->m_timeLineEdit->text().toInt(&isOk);
            if(!isOk)
            {
                QMessageBox::warning(this,QString("提示"),QString("输入的信息不合法!"));
            }
            else
            {
                m_timer->start(ms);
                m_startPushButton->setText("停止");
                m_timeLineEdit->setReadOnly(true);
            }
        }
        else
        {
            m_timer->stop();
            m_startPushButton->setText("开始");
            m_timeLineEdit->setReadOnly(false);
        }
    }
}

  (4)客户端的编写

    新建QT GUI程序,类名为UDPClient,基类为QWidget,不需要创建UI文件

  (5)在udpclient.h中写以下成员

#ifndef UDPCLIENT_H
#define UDPCLIENT_H

#include <QWidget>
#include <QUdpSocket>
#include <QLineEdit>
#include <QLabel>
#include <QHostAddress>
#include <QPushButton>
#include <QTextEdit>
#include <QVBoxLayout>
class UDPClient : public QWidget
{
    Q_OBJECT
public:
    UDPClient(QWidget *parent = nullptr);
    ~UDPClient();
private slots:
    void onReadyRead();
    void onClose();
private:
    QTextEdit*m_textEdit;
    QPushButton*m_closePushButton;
    QVBoxLayout*m_mainLayout;
private:
    QUdpSocket*m_clientSocket;
};
#endif // UDPCLIENT_H

  (6)在udpclient.cpp中进行实现

#include "udpclient.h"

UDPClient::UDPClient(QWidget *parent)
    : QWidget(parent)
{
    m_mainLayout = new QVBoxLayout(this);
    m_textEdit = new QTextEdit;
    m_textEdit->setReadOnly(true);
    m_closePushButton = new QPushButton("关闭");
    m_clientSocket = new QUdpSocket(this);
    m_mainLayout->addWidget(m_textEdit);
    m_mainLayout->addWidget(m_closePushButton);

    m_clientSocket->bind(9999);//为客户端也绑定一个端口
    connect(this->m_clientSocket,&QUdpSocket::readyRead,this,&UDPClient::onReadyRead);
    connect(this->m_closePushButton,&QPushButton::clicked,this,&UDPClient::onClose);
}

UDPClient::~UDPClient()
{
    delete m_textEdit;
    m_textEdit = nullptr;
    delete m_closePushButton;
    m_closePushButton = nullptr;
    delete m_mainLayout;
    m_mainLayout = nullptr;
    delete m_clientSocket;
    m_clientSocket = nullptr;
}

void UDPClient::onReadyRead()
{
    while(m_clientSocket->hasPendingDatagrams())
    {
        char data[1024] = {0};
        QHostAddress serverHost;
        quint16 serverPort;
        m_clientSocket->readDatagram(data,sizeof (data),&serverHost,&serverPort);
        m_textEdit->insertPlainText(QString("%1(%2)%3").arg(serverHost.toString()).arg(serverPort).arg(data));
    }
}

void UDPClient::onClose()
{
    this->close();
}

  (7)运行

  本节代码:https://files.cnblogs.com/files/blogs/792763/UDP.zip?t=1720537969&download=true

posted @ 2024-07-09 23:13  蜡笔小新Pointer  阅读(44)  评论(0)    收藏  举报