三十八、Qt缓存之UDP及其使用案例

一、乱七八糟知识点

QUdpSocket::ShareAddress和QUdpSocket::ReuseAddressHint

//ShareAddress,允许其他的服务(进程)去绑定这个IP和端口
//ReuseAddressHint为失败后立即使用,和SO_REUSEADDR同等功效
udpSocket->bind(port, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);

SO_REUSEADD详解
这个套接字选项通知内核,如果端口忙,但TCP状态位于 TIME_WAIT ,可以重用端口。如果端口忙,而TCP状态位于其他状态,重用端口时依旧得到一个错误信息,指明"地址已经使用中"。如果你的服务程序停止后想立即重启,而新套接字依旧使用同一端口,此时SO_REUSEADDR 选项非常有用。必须意识到,此时任何非期望数据到达,都可能导致服务程序反应混乱,不过这只是一种可能,事实上很不可能。

二、通信案例

相对于TCP,UDP没有绝对的服务端/客户端之分,谁都可以是服务端,也都可以是客户端,下面的案例,为了演示方便,区分出接收端/发送端

(一)发送端

finalenum.h

#ifndef FINALENUM_H
#define FINALENUM_H

//const int UDP_MAX_SIZE = 1024;

#define PORT 555

enum MessageType {
    Cm_Info = 0
};

#endif // FINALENUM_H

sender.h

#ifndef SENDER_H
#define SENDER_H

#include 
#include 
#include 

#include "finalenum.h"

class Sender : public QObject
{
    Q_OBJECT

public:
    explicit Sender(QObject *parent = nullptr);
    ~Sender();
    void sendCommand(MessageType msgType, QString msg);

private:
    QUdpSocket *udp;
};

#endif // SENDER_H

sender.cpp

#include "sender.h"

Sender::Sender(QObject *parent) : QObject(parent)
{
    udp = new QUdpSocket;
}

Sender::~Sender()
{
    udp->abort();
    udp->deleteLater();
}
//发送请求
void Sender::sendCommand(MessageType msgType, QString msg)
{
    //字节数组
    QByteArray dataGram;
    //QDataStream类是将序列化的二进制数据送到io设备,因为其属性为只写
    QDataStream out(&dataGram, QIODevice::WriteOnly); //out为待发送
    
    out << msgType;

    //以下都只是把数据存在out里,但是out又在data中,所以最后发送的还是data
    //根据消息类型,来方便服务端处理识别相应的请求
    switch (msgType) {
    case Cm_Info:
        {
            out << msg;
            break;
        }
    }
    //压缩字节码
    QByteArray compressedDatas = qCompress(dataGram);
    udp->writeDatagram(compressedDatas, compressedDatas.length(), QHostAddress::Broadcast, PORT);
}

(二)接收端

finalenum.h

#ifndef FINALENUM_H
#define FINALENUM_H

//const int UDP_MAX_SIZE = 1024;

#define PORT 555

enum MessageType {
    Cm_Info = 0
};

#endif // FINALENUM_H

receiver.h

#ifndef RECEIVER_H
#define RECEIVER_H

#include 
#include 

class Receiver : public QObject
{
    Q_OBJECT
public:
    explicit Receiver(QObject *parent = nullptr);
    ~Receiver();

private:
    QUdpSocket *udpSocket;

private slots:
    void processPendingDatagram();

};

#endif // RECEIVER_H

receiver.cpp

#include "receiver.h"

#include "finalenum.h"
#include 
#include 
#include 

Receiver::Receiver(QObject *parent) :
    QObject(parent)
{
    udpSocket = new QUdpSocket(this);
    //绑定,第一个参数为端口号,第二个表示允许其它地址链接该广播
    udpSocket->bind(PORT, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
    connect(udpSocket, SIGNAL(readyRead()), this, SLOT(processPendingDatagram()));
}

Receiver::~Receiver()
{
    udpSocket->abort();
    udpSocket->deleteLater();
}
//处理请求
void Receiver::processPendingDatagram()
{
    QByteArray dataGram;
    while (udpSocket->hasPendingDatagrams()) {

        dataGram.resize(udpSocket->pendingDatagramSize());

        QHostAddress sender;
        quint16 senderPort;

        udpSocket->readDatagram(dataGram.data(),
                                dataGram.size(),
                                &sender,
                                &senderPort);
        //解压字节码
        QByteArray unCompressedData = qUncompress(dataGram);

        QDataStream in(&unCompressedData, QIODevice::ReadOnly);

        int msgType;

        in >> msgType;
        //根据消息类型,来判断什么类型的请求,做出对应的处理
        switch (msgType) {
        case Cm_Info:
            {
                QString msg;
                in >> msg;
                QMessageBox::information(NULL, "消息", msg);
                break;
            }
        }
    }
}

你可能感兴趣的:(QT)