一、乱七八糟知识点

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 <QObject>
#include <QUdpSocket>
#include <QDataStream>

#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 <QObject>
#include <QUdpSocket>

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 <QDebug>
#include <QDataStream>
#include <QMessageBox>

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