UDP是一种是一种轻量级、不可靠、面向数据报的无连接协议。当可靠性不重要时,可以使用它。
QUdpSocket 是 QAbstractSocket 的一个子类,所以拥有QAbstractSocket的各种方法,允许您发送和接收 UDP 数据报。
QAbstractSocket的各种函数已经在上一篇文章Qt TCP中列出,这里就不列举了。
QUdpSocket的函数:
writeDatagram() | 将数据报数据报发送到主机地址主机和端口端口。 如果成功,该函数返回发送的字节数,如果遇到错误,则返回 -1。 一般发送小于512字节的数据报。 这种方法可能会掉包,所以一般使用write()发送数据报 |
readDatagram() | 接收数据报,需要指定接收大小,成功时返回数据报的大小;否则返回 -1。 一般会先使用pendingDatagramSize()来确定数据报大小,否则多余部分将丢失。 |
receiveDatagram() | 接收数据报,并在QNetworkDatagram对象中返回该数据报,以及发送方的主机地址和端口,如果 maxSize 太小,则数据报的其余部分将丢失。如果 maxSize 为 0,则将丢弃数据报。如果 maxSize 为 -1(默认值),此函数将尝试读取整个数据报(Qt 5.8引入) |
pendingDatagramSize() | 返回第一个挂起的UDP数据报的大小,如果没有可用的数据报,此函数返回 -1 |
hasPendingDatagrams() | 判断是否有等待的数据报 |
joinMulticastGroup() | 在操作系统选择的默认接口上加入由组地址指定的多播组,套接字必须处于连接状态 |
leaveMulticastGroup() | 将指定的多播组保留在操作系统中 |
multicastInterface() | 返回多播数据报的传出接口的接口 |
setMulticastInterface() | 设置多播数据报的接口 |
注意: 收到 readyRead() 信号时,应读取传入的数据报,否则不会为下一个数据报发出此信号。
UDP的构建方法:
UDP没有分客户端和服务器端,而是直接使用接收端和发送端,直接使用QUdpSocket。
- 创建一个QUdpSocket
- 使用bind()函数指定IP和端口号(接收时)
- 使用writeDatagram()发送数据报(需要绑定IP,和端口号)
- 使用readDatagram()接收数据报
bind函数:
bind(const QHostAddress & address,quint 16 port=0,QAbstractSocket::BindMode
mode=DefaultForPlatfrom)
该函数可以指定IP,端口号,绑定模式
bind(quint 16 port=0,QAbstractSocket::BindModemode=DefaultForPlatfrom)
该函数的一个重载,可以不指定IP
QAbstractSocket::BindMode
QAbstractSocket::ShareAddress | 允许其他服务绑定到同一地址和端口。 |
QAbstractSocket::DontShareAddress | 以独占方式绑定地址和端口,不允许重新绑定其他服务。 |
QAbstractSocket::ReuseAddressHint | 向 QAbstractSocket提供提示,即使地址和端口已由另一个套接字绑定,它也应尝试重新绑定服务。 |
QAbstractSocket::DefaultForPlatform | 当前平台的默认选项。在Unix和macOS上,这相当于(DontShareAddress + ReuseAddressHint),而在Windows上,它相当于ShareAddress。 |
创建一个可接收可发送的UDP传输:
1.在pro文件中添加
QT +=network
2.ui界面中添加以下控件:
3. .h文件中添加以下代码
#ifndef SOCKET_H
#define SOCKET_H
#include <QWidget>
class QUdpSocket;
QT_BEGIN_NAMESPACE
namespace Ui { class socket; }
QT_END_NAMESPACE
class socket : public QWidget
{
Q_OBJECT
public:
socket(QWidget *parent = nullptr);
~socket();
private slots:
void on_pushButton_clicked();//这两个是ui界面中转到槽生成的
void on_pushButton_2_clicked();
private:
Ui::socket *ui;
QUdpSocket * sk;//一个QUdpSocket对象
};
#endif // SOCKET_H
4. .cpp文件内容
#include "ui_socket.h"
#include<QUdpSocket>
#include<QMessageBox>
socket::socket(QWidget *parent)
: QWidget(parent)
, ui(new Ui::socket)
{
ui->setupUi(this);
sk=new QUdpSocket(this);
connect(sk,&QUdpSocket::readyRead,[=]()//如有新的数据报
{
while(sk->hasPendingDatagrams())//判断是否有可获取的数据报
{
QByteArray bta;//接收数据
bta.resize(sk->pendingDatagramSize());//重置大小
sk->readDatagram(bta.data(),bta.size());//获取数据
ui->textEdit->append(QString("陌生人:%1").arg(bta.data()));//放在textEdit中显示
}
});
}
socket::~socket()
{
delete ui;
}
void socket::on_pushButton_clicked()//发送数据
{
QByteArray bta=ui->lineEdit2->text().toUtf8();//获取内容
//发送数据,内容,大小,IP,端口号 QHostAddress::Broadcast(广播地址)255.255.255.255
sk->writeDatagram(bta,bta.size(),QHostAddress::Broadcast,ui->lineEdit1->text().toInt());
ui->textEdit->append(QString("自己:%1").arg(ui->lineEdit2->text()));
}
void socket::on_pushButton_2_clicked()//绑定接收端口
{
sk->bind(ui->lineEdit3->text().toInt(),QUdpSocket::ShareAddress);//绑定端口
}
然后再创建一个项目,使用一摸一样的代码:
分别运行这两个项目:
点击该按键,可以选择项目运行。
运行演示为: