1. UDP通信概述
UDP是无连接、不可靠、面向数据报(datagram)的协议,可以应用于对可靠性要求不高的场合。与TCP通信不同,UDP通信无需预先建立持久的socket连接,UDP每次发送数据报都需要指定目标地址和端口。
QUdpSocket以数据报传输数据,而不是以连续的数据流。发送数据报使用函数 QUdpSocket::writeDatagram(),数据报的长度一般少于512字节,每个数据报包含发送者和接收者的IP地址和端口等信息。
UDP数据接收,首先要使用QUdpSocket::bind()绑定一个端口,绑定端口后,socket的状态会变为已绑定状态“BoundState”。
当有数据报传入时,QudpSocket会自动发射readyRead()信号,在其槽函数中使用QUdpSocket::readDatagram()进行数据读取。abort()为解除绑定,解除后socket状态变为未连接状态“UnconnectedState”。
2. UDP消息传送的三种模式
单播模式(unicast):一个UDP客户端发送数据报到指定地址和端口的另一UDP客户端,是一对一的数据传输。
广播模式(broadcast):一个UDP客户端发出的数据报,在同一网络范围内其他所有的UDP客户端都可以收到。QUdpSocket支持IPv4广播。需要在数据报中指定接收端地址为QHostAddress::Broadcast,一般的广播地址是255.255.255.255。
组播模式(multicast):UDP客户端加入到另一个组播IP地址的多播组,成员向组播地址发送的数据报,其加入组播的所有成员都可以接收到,类似于QQ群功能。QUdpSocket::joinMulticastGroup()函数实现加入多播组的功能。
QUdpSocket::leaveMulticastGroup()函数实现
在单播、广播和多播模式下,UDP程序都是对等的,不像TCP通信分为客户端和服务端。
TCP通信只有单播模式。UDP通信虽然不能保证数据传输的准确性,但是具有灵活性,一般的即时通信软件都是基于UDP通信的。
3. QUdpSocket类的接口函数
bool bind(quint16 port = 0) 为UDP通信绑定一个端口
qint64 writeDatagram(QByteArray& datagram, QHostAddress& host, quint16 port) 向目标地址和端口的UDP客户端发送数据报,返回成功发送的字节数,数据报的长度一般不超过512字节。
bool hasPendingDatagrams() 当至少有一个数据报需要读取时,返回true
qint64 pendingDatagramSize() 返回第一个待读取的数据报的大小
qint64 readDatagram(char* data, qint64 maxSize) 读取一个数据报,返回成功读取的字节数
qint64 readDatagram(char* data, qint64 maxSize, QHostAddress* address, quint16* port) 读取一个数据报,返回成功读取的字节数。发送方的主机地址和端口存储在*address和*port中(除非指针为0)
bool joinMulticastGroup(QHostAddress& groupAddress) 加入一个多播组
bool leaveMulticastGroup(QHostAddress& groupAddress) 离开一个多播组
void abort() 终止当前连接并重置套接字。通常在析构函数中写入。与disconnectFromHost()不同,该函数立即关闭套接字,丢弃写入缓冲区中的任何挂起数据。
原文链接:https://blog.csdn.net/WL0616/article/details/129050373
4.UDP对话小案例
实现发送和接收端互相发信息,类似QQ (界面使用UI设计)
4.1.接收端
receiver.h
#ifndef RECESIVER_H
#define RECESIVER_H
#include <QWidget>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Recesiver; }
QT_END_NAMESPACE
class Recesiver : public QWidget
{
Q_OBJECT
public:
Recesiver(QWidget *parent = nullptr);
~Recesiver();
private slots:
void on_pushButton_2_clicked(); //启动槽函数
void start();
void on_pushButton_clicked();
private:
Ui::Recesiver *ui;
QUdpSocket *receiver;
};
#endif // RECESIVER_H
receiver.cpp
#include "recesiver.h"
#include "ui_recesiver.h"
Recesiver::Recesiver(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Recesiver)
{
ui->setupUi(this);
setWindowTitle(QStringLiteral("接收端"));
ui->lineEdit->setText("127.0.0.1");
receiver =new QUdpSocket(this);
}
Recesiver::~Recesiver()
{
delete ui;
}
//接收信息
void Recesiver::start()
{
QByteArray datagram;
datagram.resize(receiver->pendingDatagramSize()); //接收到的数据的长度
receiver->readDatagram(datagram.data(),datagram.size());
ui->textEdit->append(QStringLiteral("对方:")+datagram);
}
//启动按钮(发送端发送信息给接收端)
void Recesiver::on_pushButton_2_clicked()
{
receiver->bind(ui->lineEdit_2->text().toInt());//设置端口号将其转为整型
connect(receiver,&QUdpSocket::readyRead,this,[&](){start();});
ui->pushButton_2->setEnabled(false);
ui->lineEdit_2->setEnabled(false);
}
//发送按钮(接收端发送信息给发送端)
void Recesiver::on_pushButton_clicked()
{
QByteArray datagram=ui->textEdit_2->toPlainText().toUtf8(); //在输入端输入发送的内容
receiver->writeDatagram(datagram.data(),datagram.size(),QHostAddress(ui->lineEdit->text()),ui->lineEdit_3->text().toInt());
//qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &host, quint16 port);
//发送数据,大小,发送主机的ip,发送主机的端口号
ui->textEdit->append(QStringLiteral("本机:")+ui->textEdit_2->toPlainText());//发送信息的具体内容在发送端的聊天记录里能体现
ui->textEdit_2->clear();
}
4.2发送端
sender.h
#ifndef SENDER_H
#define SENDER_H
#include <QWidget>
#include <QUdpSocket>
QT_BEGIN_NAMESPACE
namespace Ui { class Sender; }
QT_END_NAMESPACE
class Sender : public QWidget
{
Q_OBJECT
public:
Sender(QWidget *parent = nullptr);
~Sender();
void start2();
private slots:
void on_pushButton_clicked();
private:
Ui::Sender *ui;
QUdpSocket *sender;
};
#endif // SENDER_H
sender.cpp
#include "sender.h"
#include "ui_sender.h"
Sender::Sender(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Sender)
{
ui->setupUi(this);
sender=new QUdpSocket(this);
setWindowTitle(QStringLiteral("发送端"));
//sender
ui->lineEdit_3->setText("888");
sender->bind(ui->lineEdit_3->text().toInt());//绑定端口号
connect(sender,&QUdpSocket::readyRead,this,[&](){start2();});
//start2();
}
void Sender::start2()
{
QByteArray datagram;
datagram.resize(sender->pendingDatagramSize()); //接收到的数据的长度
sender->readDatagram(datagram.data(),datagram.size());
ui->textEdit->append(QStringLiteral("对方:")+datagram);
}
Sender::~Sender()
{
delete ui;
}
//发送按钮 发送信息给接收端
void Sender::on_pushButton_clicked()
{
QByteArray datagram=ui->textEdit_2->toPlainText().toUtf8(); //在输入端输入发送的内容
sender->writeDatagram(datagram.data(),datagram.size(),QHostAddress(ui->lineEdit->text()),ui->lineEdit_2->text().toInt());
//qint64 writeDatagram(const char *data, qint64 len, const QHostAddress &host, quint16 port);
//发送数据,大小,发送主机的ip,发送主机的端口号
ui->textEdit->append(QStringLiteral("