一、乱七八糟知识点
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;
}
}
}
}