概述
UDP (User Datagram Protocol)
是一种简单的传输层协议。与TCP不同,UDP不提供可靠的数据传输和错误检测机制。UDP主要用于那些对实时性要求较高、对数据传输可靠性要求较低的应用,如音频、视频、实时游戏等。
UDP
使用无连接的数据报传输模式。在传输数据之前,发送方和接收方不需要建立一个持久的连接,只需发送数据报文即可。每个数据报文都是独立的,没有前后关系,因此也不必保证按照发送的顺序接收。
UDP协议的特点包括:
- 无连接性:发送方和接收方之间不需要建立和维护连接。
- 快速性:由于无连接性,UDP的传输速度相对较快。
- 无可靠性保证:UDP不提供可靠的数据传输,不保证数据的完整性和正确性。
- 简单性:UDP的协议头部较短,占用的数据传输量较小。
UDP数据传输示意图:
QUdpSocket
QUdpSocket类
提供了UDP套接字。
QUdpSocket
是QAbstractSocket
的一个子类,它允许发送和接收UDP数据报。
这里的socket
就是所谓的套接字,简单地说,就是一个Ip地址+一个Port端口号。
使用这个类最常见的方法是使用bind()
绑定到一个地址和端口,然后调用writeDatagram()
和readDatagram()
/ receiveDatagram()
来传输数据。如果想使用标准的QIODevice函数read(), readLine(), write()等,必须首先通过调用connectToHost()
将套接字直接连接到对等体。
套接字每次将数据报写入网络时都会发出bytesWritten()
信号。如果您只想发送数据报,则不需要调用bind()
。
每当数据报到达时,就会发出readyRead()
信号。在这种情况下,hasPendingDatagrams()
返回true。调用pendingDatagramSize()来获取第一个挂起数据报的大小,并调用readDatagram()
或receiveDatagram()
来读取它。
注意:当接收readyRead()
信号时,应该读取传入的数据报,否则将不会为下一个数据报发出该信号。
QUdpSocket
支持IPv4广播,IPv4广播是一种在IPv4网络中向同一网络中的所有主机发送数据的方式。
在IPv4网络中,广播地址是一个特殊的IP地址,用于指示对应网络中的所有主机。
IPv4广播使用的是一个特定的IP地址,即网络地址的所有主机位都为1的情况下,主机地址为0。例如,在一个192.168.0.0/24的网络中,广播地址为192.168.0.255。
使用IPv4广播,可以将数据一次性发送到同一网络中的所有主机,而不需要逐个发送给每个主机。这在某些应用中非常有用,例如在局域网中通知所有主机进行某项操作,或者在DHCP协议中分发IP地址等。
然而,由于IPv4广播发送的数据会被同一网络中的所有主机接收,这也可能会造成一些安全和性能问题。因此,在IPv4网络中广播的使用需要谨慎,并需要对广播进行适当的限制和控制。
例如:
void Server::initSocket()
{
udpSocket = new QUdpSocket(this);
udpSocket->bind(QHostAddress::LocalHost, 7755);
connect(udpSocket, SIGNAL(readyRead()),
this, SLOT(readPendingDatagrams()));
}
void Server::readPendingDatagrams()
{
while (udpSocket->hasPendingDatagrams()) {
QNetworkDatagram datagram = udpSocket->receiveDatagram();
processTheDatagram(datagram);
}
}
QUdpSocket
还支持UDP组播。使用joinMulticastGroup()
和leaveMulticastGroup()
来控制组成员,使用QAbstractSocket::MulticastTtlOption
和QAbstractSocket::MulticastLoopbackOption
来设置TTL
和loopback
套接字选项。使用setMulticastInterface()
控制组播数据报的出接口,使用multicastInterface()
进行查询。
使用QUdpSocket
,还可以使用connectToHost()与UDP服务器建立虚拟连接,然后使用read()和write()交换数据报,而无需指定每个数据报的接收者。
示例
以下是一个发送者,一个接收者,发送者定时发送数据,接收者进行显示
sender.h
#include <QWidget>
QT_BEGIN_NAMESPACE
class QDialogButtonBox;
class QLabel;
class QPushButton;
class QTimer;
class QUdpSocket;
QT_END_NAMESPACE
class Sender : public QWidget
{
Q_OBJECT
public:
Sender(QWidget *parent = 0);
private slots:
void startBroadcasting();
void broadcastDatagram();
private:
QLabel *statusLabel;
QPushButton *startButton;
QPushButton *quitButton;
QDialogButtonBox *buttonBox;
QUdpSocket *udpSocket;
QTimer *timer;
int messageNo;
};
sender.cpp
#include <QtWidgets>
#include <QtNetwork>
#include "sender.h"
Sender::Sender(QWidget *parent)
: QWidget(parent)
{
statusLabel = new QLabel(tr("绑定 端口 45454"));
statusLabel->setWordWrap(true);
startButton = new QPushButton(tr("&Start"));
quitButton = new QPushButton(tr("&Quit"));
buttonBox = new QDialogButtonBox;
buttonBox->addButton(startButton, QDialogButtonBox::ActionRole);
buttonBox->addButton(quitButton, QDialogButtonBox::RejectRole);
timer = new QTimer(this);
udpSocket = new QUdpSocket(this);
messageNo = 1;
connect(startButton, SIGNAL(clicked()), this, SLOT(startBroadcasting()));
connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));
connect(timer, SIGNAL(timeout()), this, SLOT(broadcastDatagram()));
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(statusLabel);
mainLayout->addWidget(buttonBox);
setLayout(mainLayout);
setWindowTitle(tr("广播发送"));
}
void Sender::startBroadcasting()
{
startButton->setEnabled(false);
timer->start(1000);
}
void Sender::broadcastDatagram()
{
statusLabel->setText(tr("现在 广播 信息 %1").arg(messageNo));
QByteArray datagram = "广播 信息 " + QByteArray::number(messageNo);
udpSocket->writeDatagram(datagram.data(), datagram.size(),
QHostAddress::Broadcast, 45454);
++messageNo;
}
receiver.h
#include <QWidget>
QT_BEGIN_NAMESPACE
class QLabel;
class QPushButton;
class QUdpSocket;
class QAction;
QT_END_NAMESPACE
class Receiver : public QWidget
{
Q_OBJECT
public:
Receiver(QWidget *parent = 0);
private slots:
void processPendingDatagrams();
private:
QLabel *statusLabel;
QPushButton *quitButton;
QUdpSocket *udpSocket;
};
receiver.cpp
#include <QtWidgets>
#include <QtNetwork>
#include "receiver.h"
Receiver::Receiver(QWidget *parent)
: QWidget(parent)
{
statusLabel = new QLabel(tr("监听 广播 信息"));
statusLabel->setWordWrap(true);
quitButton = new QPushButton(tr("&Quit"));
udpSocket = new QUdpSocket(this);
udpSocket->bind(45454, QUdpSocket::ShareAddress);
connect(udpSocket, SIGNAL(readyRead()),
this, SLOT(processPendingDatagrams()));
connect(quitButton, SIGNAL(clicked()), this, SLOT(close()));
QHBoxLayout *buttonLayout = new QHBoxLayout;
buttonLayout->addStretch(1);
buttonLayout->addWidget(quitButton);
buttonLayout->addStretch(1);
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(statusLabel);
mainLayout->addLayout(buttonLayout);
setLayout(mainLayout);
setWindowTitle(tr("广播 接收"));
}
void Receiver::processPendingDatagrams()
{
while (udpSocket->hasPendingDatagrams()) {
QByteArray datagram;
datagram.resize(udpSocket->pendingDatagramSize());
udpSocket->readDatagram(datagram.data(), datagram.size());
statusLabel->setText(tr("接收 数据: \"%1\"")
.arg(datagram.data()));
}
}
效果
默认显示如下:
当点击发送时:
结论
青春就像一只容器,装满了不安躁动青涩与偶尔的疯狂
。