QT系列— UDP应用篇

QT UDP应用篇

1、开发环境

PC操作系统: WIN7

QT开发环境:Qt Creator5.5

2、开发目标

如下图所示,开发一个UDP工具,实现如下功能:

1、设置UDP通信目的IP地址、目的端口号和绑定的本地端口号。

2、支持接收消息,支持文本和十六进制显示。

3、支持发送消息,支持文本和十六进制发送。

4、支持接收消息的清空,支持UDP连接的打开和关闭功能。
在这里插入图片描述

3、UDP原理

使用Qt提供的QUdpSocket进行UDP通信。在UDP方式下,客户端并不与服务器建立连接,它只负责调用发送函数向服务器发送数据。类似的服务器也不从客户端接收连接,只负责调用接收函数,等待来自客户端的数据的到达。

在UDP通信中,服务器端和客户端的概念显得有些淡化,两部分做的工作都大致相同:

(1)创建套接字

(2)绑定套接字

在UDP中如果需要接收数据则需要对套接字进行绑定,只发送数据则不需要对套接字进行绑定。

通过调用bind函数将套接字绑定到指定端口上。

(3)接收数据:使用readDatagram()接收数据,函数声明如下:

qint64 readDatagram(char * data, qint64 maxSize, QHostAddress * address = 0, quint16 * port = 0)

参数:

u data: 接收数据的缓存地址

u maxSize: 缓存接收的最大字节数

u address: 数据发送方的地址(一般使用提供的默认值)

u port: 数据发送方的端口号(一般使用提供的默认值)

使用pendingDatagramSize()可以获取到将要接收的数据的大小,根据该函数返回值来准备对应大小的内存空间存放将要接收的数据。

(4)发送数据: 使用writeDatagram()函数发送数据,函数声明如下:

qint64 writeDatagram(const QByteArray & datagram, const QHostAddress & host, quint16 port)

参数:

u datagram:要发送的字符串

u host:数据接收方的地址

u port:数据接收方的端口号

广播

在使用QUdpSocket类的writeDatagram()函数发送数据的时候,其中第二个参数host应该指定为广播地址:QHostAddress::Broadcast此设置相当于QHostAddress(“255.255.255.255”)

使用UDP广播的的特点:

使用UDP进行广播,局域网内的其他的UDP用户全部可以收到广播的消息

UDP广播只能在局域网范围内使用

组播

我们再使用广播发送消息的时候会发送给所有用户,但是有些用户是不想接受消息的,这时候我们就应该使用组播,接收方只有先注册到组播地址中才能收到组播消息,否则则接受不到消息。另外组播是可以在Internet中使用的。在使用QUdpSocket类的writeDatagram()函数发送数据的时候,其中第二个参数host应该指定为组播地址,关于组播地址的分类:

224.0.0.0~224.0.0.255为预留的组播地址(永久组地址),地址224.0.0.0保留,其它地址供路由协议使用;

224.0.1.0~224.0.1.255是公用组播地址,可以用于Internet;

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

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

注册加入到组播地址需要使用QUdpSocket类的成员函数:

bool joinMulticastGroup(const QHostAddress & groupAddress)

4、示例代码

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

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    setWindowTitle("UDP工具");

    //分配空间,分配父对象
    udpSocket = new QUdpSocket(this);

    //利用信号槽机制,当对方发过来数据,通过readyRead()信号触发收数据
    connect(udpSocket, &QUdpSocket::readyRead, this, &Widget::recvData);

    //接收消息文本编辑框设置为只读模式
    ui->textEditRecv->setReadOnly(true);
}

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

// 将字符转换成十六进制字符串
void Widget::char2HexString(char *data, int len, QString &str)
{

    unsigned char high_hex;
    unsigned char low_hex;
    unsigned char *pData = NULL;

    //强制转换char*-->unsigned char* 防止char溢出
    pData = (unsigned char *)data;

    for (int i = 0; i < len; i++)
    {
        high_hex = pData[i] >> 4;
        low_hex  = pData[i] & 0x0F;

        if (high_hex > 9)
        {
            high_hex = high_hex + '0' + 7;
        }
        else
        {
            high_hex = high_hex + '0';
        }

        if(low_hex > 9)
        {
            low_hex = low_hex + '0' + 7;
        }
        else
        {
            low_hex = low_hex + '0';
        }

        str += high_hex;
        str += low_hex;
        str += " ";
    }
}

void Widget::recvData()
{
    char data[4096] = {0};
    QHostAddress cliAddr;
    quint16 cliPort;
    QString textString;
    QString hexString;

    bHexRecv = ui->boxhexRecv->isChecked();

    //UDP数据报文有效,等待被读
    while (udpSocket->hasPendingDatagrams())
    {
        //读UDP数据报文
        qint64 len = udpSocket->readDatagram(data, sizeof(data), &cliAddr, &cliPort);

        if (len > 0)
        {
            //十六进制显示
            if (true == bHexRecv)
            {
                char2HexString(data, len, hexString);
                QString str = QString("[%1:%2] ")
                        .arg(cliAddr.toString())
                        .arg(cliPort);
                textString = str + hexString;
                ui->textEditRecv->append(textString.toLower());
            }
            else
            {
                textString = QString("[%1:%2] %3\n")
                                .arg(cliAddr.toString())
                                .arg(cliPort)
                                .arg(data);
                ui->textEditRecv->append(textString);
            }
        }

        //将textEdit的光标移至最后,显示最后收到的数据
        ui->textEditRecv->moveCursor(QTextCursor::End);
    }
}

void Widget::on_buttonOpen_clicked()
{
    //绑定本地端口
    quint16 localPort = ui->lineEditLocalPort->text().toUShort();
    udpSocket->bind(QHostAddress::AnyIPv4, localPort);
    ui->buttonOpen->setDisabled(true);
}

void Widget::on_buttonClose_clicked()
{
    //关闭Socket
    udpSocket->close();
    ui->buttonOpen->setEnabled(true);
}

// 将十六进制字符串转换成字符
void Widget::hexString2Char(QString input_data, unsigned char *convert_data)
{
    int len = 0;

    QByteArray bytes = input_data.toLower().toLocal8Bit();
    int strLen = input_data.length();

    for (int i = 0; i < strLen; i++)
    {
        unsigned char high_value = 0;
        unsigned char low_value = 0;

        if (bytes.data()[i] >= 'a')
        {
            high_value = bytes.data()[i] - 'a' + 10;
        }
        else
        {
            high_value = bytes.data()[i] - '0';
        }

        if (bytes.data()[i + 1] >= 'a')
        {
            low_value = bytes.data()[i + 1] - 'a' + 10;
        }
        else
        {
            low_value = bytes.data()[i + 1] - '0';
        }

        unsigned char data = high_value * 16 + low_value;
        convert_data[len++] = data;

        i++;
        if (i + 2 < strLen)
        {
            i += 1;
        }
    }
}

void Widget::on_buttonSend_clicked()
{
    QString ip = ui->lineEditIp->text();
    quint16 peerPort = ui->lineEditPort->text().toUShort();

    QString str = ui->textEditSend->toPlainText();

    bHexSend = ui->boxhexSend->isChecked();
    //十六进制字符发送
    if (true == bHexSend)
    {
        unsigned char data[4096] = {'\0'};
        hexString2Char(str, data);
        udpSocket->writeDatagram(QByteArray((char *)data), QHostAddress(ip), peerPort);

    }
    else
    {
       udpSocket->writeDatagram(str.toUtf8(), QHostAddress(ip), peerPort);
    }
}


void Widget::on_buttonClearRecv_clicked()
{
    //清除textEdit文本框内容
    ui->textEditRecv->clear();
}

5、应用演示

(1)UDP工具发送文本消息演示如下图所示。
在这里插入图片描述
在这里插入图片描述

(2)UDP工具发送十六进制字符消息演示如下图所示。
在这里插入图片描述
在这里插入图片描述
(3)UDP工具接收消息文本显示如下图所示。
在这里插入图片描述
在这里插入图片描述
(4)UDP工具接收消息十六进制显示如下图所示。
在这里插入图片描述
在这里插入图片描述
至此,QT UDP工具的示例基本达到开发目标。当然,示例代码还有需要优化的地方,需要不断完善。欢迎读者修改使用。

  • 3
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
Qt是一个跨平台的C++应用程序开发框架,它提供了一系列的库和工具,可以帮助开发者简化应用程序的开发过程和代码维护。 在Qt中,可以使用Qt网络模块来实现基于UDP网络通信UDP(User Datagram Protocol)是一种无连接的传输层协议,它可以快速地将数据包发送到目标主机,但不保证数据包的可靠性和有序性。 Qt提供了QUdpSocket类来实现UDP通信。使用QUdpSocket,我们可以创建一个UDP套接字,并通过绑定到指定的IP地址和端口号来监听UDP报文的接收。当UDP报文到达时,可以通过槽函数来处理接收到的数据。另外,我们还可以使用writeDatagram()函数来发送UDP报文。 以下是一个简单的UDP通信的示例代码: ```cpp // 创建一个UDP套接字 QUdpSocket udpSocket; // 绑定到本地的IP地址和端口号 udpSocket.bind(QHostAddress::LocalHost, 1234); // 接收到数据报文时的槽函数 connect(&udpSocket, &QUdpSocket::readyRead, [=]() { QByteArray datagram; while(udpSocket.hasPendingDatagrams()) { datagram.resize(udpSocket.pendingDatagramSize()); udpSocket.readDatagram(datagram.data(), datagram.size()); // 处理接收到的数据报文 // ... } }); // 发送数据报文 QByteArray datagram = "Hello, UDP!"; udpSocket.writeDatagram(datagram, QHostAddress::LocalHost, 5678); ``` 上述代码中,我们创建了一个UDP套接字udpSocket,并将其绑定到本地的IP地址和端口号,用于监听UDP报文的接收。当接收到UDP报文时,会通过readyRead信号触发槽函数进行处理。我们还通过writeDatagram函数发送了一段数据报文。 总结来说,Qt提供了丰富的网络编程功能,可以很方便地实现基于UDP网络通信。使用Qt的网络模块,我们可以轻松地编写跨平台的UDP应用程序。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CPUOS2010

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值