1:UDP通信概述
UDP(User Datagram Protocol,用户数据报协议)是轻量的,不可靠的,面向数据报,无连接的协议。两个UDP之间通信无需建立持久的连接,每次发送数据都需要指定目标地址和端口。
UDP分为单播,广播,组播三种模式。在单播,广播,组播的模式下,UDP程序都是对等的。
1.1 单播
一个UDP客户端发送的数据报只能发送到另一个指定的地址和端口的UDP客户端,是一对一数据的传输。
1.2 广播
一个UDP客户端发出的数据报,在同一网络范围内其他所有的UDP客户端都可以收到,QUdpSocket支持IPV4广播。要获取广播数据只需要在数据报中接收端地址为QHostAddress::Broadcast,一般的广播地址是:255.255.255.255
1.3 组播
组播也称为多播,UDP客户端加入到另一个组播IP地址指定的多播组,成员向组播地址发送的数据报组内成员都可以接收到,类似于QQ群。QUdpSocket::joinMulticastGroup()函数实现加入多播组功能,加入多播组后,UDP数据的手法和正常的都一样。关于组播的ip地址,有以下约定
- 244.0.0.0 - 244.0.0.255为预留的组播地址(永久组地址),地址244.0.0.0保留不进行分配,其他地址路由协议使用。
- 224.0.1.0 - 224.0.1.255 是公用组播地址,可以用于Internet。
- 224.0.2.0 - 238.255.255.255 为用户可用的组播地址(临时组地址),全网范围内有效。
- 239.0.0.0 - 239.255.255.255 为本地管理的组播地址,在特定的本地范围内可用。
因此本次测试使用的地址范围是:239.0.0.0 - 239.255.255.255 leaveMulticastGroup()使得一个主机离开多播组,oinMulticastGroup()函数实现加入多播组功能。
2 单播和广播例子
页面设计如下:
#pragma once
#include <QtWidgets/QWidget>
#include <QtNetwork>
#include <QDebug>
#include <QHostInfo>
#include <QAbstractSocket>
#include <QUdpSocket>
#include <QByteArray>
#include "ui_QtWidgetsApplicationUDP.h"
class QtWidgetsApplicationUDP : public QWidget
{
Q_OBJECT
public:
QtWidgetsApplicationUDP(QWidget *parent = Q_NULLPTR);
private:
Ui::QtWidgetsApplicationUDPClass ui;
QUdpSocket* m_udpSocket = nullptr;
private:
QString getLocalIP();
private slots:
void onSocketStateChanged(QAbstractSocket::SocketState socketState);
void onSocketReadyRead();
void on_clearText_clicked();
void on_exit_clicked();
void on_actStart_clicked();
void on_actStop_clicked();
void on_btnSend_clicked();
void on_btnBroadcast_clicked();
};
#include "QtWidgetsApplicationUDP.h"
QtWidgetsApplicationUDP::QtWidgetsApplicationUDP(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
QString _loaclIP = getLocalIP();
this->setWindowTitle(this->windowTitle() + u8"本机IP:" + _loaclIP);
ui.lineEdit->setText(_loaclIP);
m_udpSocket = new QUdpSocket(this);
onSocketStateChanged(m_udpSocket->state());
connect(m_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
}
QString QtWidgetsApplicationUDP::getLocalIP()
{
QString _hostName = QHostInfo::localHostName();// 本地主机名称
QHostInfo _hostInfo = QHostInfo::fromName(_hostName);
QString _loacalIP = "";
QList<QHostAddress> _address = _hostInfo.addresses();
if (!_address.isEmpty())
{
for (int i = 0; i < _address.count(); i++)
{
QHostAddress _aHost = _address.at(i);
if (QAbstractSocket::IPv4Protocol == _aHost.protocol())
{
_loacalIP = _aHost.toString();
break;
}
}
}
qDebug() << _loacalIP;
return _loacalIP;
}
void QtWidgetsApplicationUDP::onSocketStateChanged(QAbstractSocket::SocketState socketState)
{
switch (socketState)
{
case QAbstractSocket::UnconnectedState:
ui.label_4->setText("UnconnectedState");
break;
case QAbstractSocket::HostLookupState:
ui.label_4->setText("HostLookupState");
break;
case QAbstractSocket::ConnectingState:
ui.label_4->setText("ConnectingState");
break;
case QAbstractSocket::ConnectedState:
ui.label_4->setText("ConnectedState");
break;
case QAbstractSocket::BoundState:
ui.label_4->setText("BoundState");
break;
case QAbstractSocket::ListeningState:
ui.label_4->setText("ListeningState");
break;
case QAbstractSocket::ClosingState:
ui.label_4->setText("ClosingState");
break;
default:
break;
}
}
void QtWidgetsApplicationUDP::onSocketReadyRead()
{
while (m_udpSocket->hasPendingDatagrams())
{
QByteArray _datagram;
_datagram.resize(m_udpSocket->pendingDatagramSize());
QHostAddress _peerAddr;
quint16 _peerPort;
m_udpSocket->readDatagram(_datagram.data(), _datagram.size(), &_peerAddr, &_peerPort);
QString _str = _datagram.data();
QString _peer = "[From " + _peerAddr.toString() + ":" + QString::number(_peerPort) + "] ";
ui.plainTextEdit->appendPlainText(_peer + _str);
}
}
void QtWidgetsApplicationUDP::on_clearText_clicked()
{
ui.plainTextEdit->clear();
}
void QtWidgetsApplicationUDP::on_exit_clicked()
{
exit(0);
}
void QtWidgetsApplicationUDP::on_actStart_clicked()
{
quint16 _port = ui.spinBox->value();
if (m_udpSocket->bind(_port))
{
ui.plainTextEdit->appendPlainText(u8"成功绑定" + QString::number(m_udpSocket->localPort()));
ui.actStart->setEnabled(false);
ui.actStop->setEnabled(true);
}
else
{
ui.plainTextEdit->appendPlainText(u8"绑定失败");
}
}
void QtWidgetsApplicationUDP::on_actStop_clicked()
{
m_udpSocket->abort();
ui.actStart->setEnabled(true);
ui.actStop->setEnabled(false);
ui.plainTextEdit->appendPlainText(u8"已解除绑定");
}
void QtWidgetsApplicationUDP::on_btnSend_clicked()
{
QString _targetIP = ui.lineEdit->text();
QHostAddress _targetdress(_targetIP);
quint16 _targetPort = ui.spinBox_2->value();
QString _msg = ui.lineEdit_2->text();
QByteArray _str = _msg.toUtf8();
m_udpSocket->writeDatagram(_str, _targetdress, _targetPort);
ui.plainTextEdit->appendPlainText("[OUT] " + _msg);
ui.lineEdit_2->clear();
ui.lineEdit_2->setFocus();
}
void QtWidgetsApplicationUDP::on_btnBroadcast_clicked()
{
quint16 _targetPort = ui.spinBox_2->value();
QString _msg = ui.lineEdit_2->text();
QByteArray _str = _msg.toUtf8();
m_udpSocket->writeDatagram(_str, QHostAddress::Broadcast, _targetPort);
ui.plainTextEdit->appendPlainText("[OUT] " + _msg);
ui.lineEdit_2->clear();
ui.lineEdit_2->setFocus();
}
操作视频:
3 组播例子
#pragma once
#include <QtWidgets/QWidget>
#include <QtNetwork>
#include <QDebug>
#include <QHostInfo>
#include <QAbstractSocket>
#include <QUdpSocket>
#include <QByteArray>
#include "ui_QtWidgetsApplicationUDPZB.h"
class QtWidgetsApplicationUDPZB : public QWidget
{
Q_OBJECT
public:
QtWidgetsApplicationUDPZB(QWidget *parent = Q_NULLPTR);
QUdpSocket* m_udpSocket = nullptr;
QHostAddress groupAdress;//组播地址
QString getLocalIP();
private:
Ui::QtWidgetsApplicationUDPZBClass ui;
private slots:
void onSocketStateChanged(QAbstractSocket::SocketState socketState);
void onSocketReadyRead();
void on_clearText_clicked();
void on_exit_clicked();
void on_actStart_clicked();
void on_actStop_clicked();
void on_btnSend_clicked();
};
#include "QtWidgetsApplicationUDPZB.h"
QtWidgetsApplicationUDPZB::QtWidgetsApplicationUDPZB(QWidget *parent)
: QWidget(parent)
{
ui.setupUi(this);
QString _loaclIP = getLocalIP();
this->setWindowTitle(this->windowTitle() + u8"本机IP:" + _loaclIP);
m_udpSocket = new QUdpSocket(this);
m_udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1);
onSocketStateChanged(m_udpSocket->state());
connect(m_udpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChanged(QAbstractSocket::SocketState)));
connect(m_udpSocket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead()));
}
QString QtWidgetsApplicationUDPZB::getLocalIP()
{
QString _hostName = QHostInfo::localHostName();// 本地主机名称
QHostInfo _hostInfo = QHostInfo::fromName(_hostName);
QString _loacalIP = "";
QList<QHostAddress> _address = _hostInfo.addresses();
if (!_address.isEmpty())
{
for (int i = 0; i < _address.count(); i++)
{
QHostAddress _aHost = _address.at(i);
if (QAbstractSocket::IPv4Protocol == _aHost.protocol())
{
_loacalIP = _aHost.toString();
break;
}
}
}
qDebug() << _loacalIP;
return _loacalIP;
}
void QtWidgetsApplicationUDPZB::onSocketStateChanged(QAbstractSocket::SocketState socketState)
{
switch (socketState)
{
case QAbstractSocket::UnconnectedState:
ui.label_4->setText("UnconnectedState");
break;
case QAbstractSocket::HostLookupState:
ui.label_4->setText("HostLookupState");
break;
case QAbstractSocket::ConnectingState:
ui.label_4->setText("ConnectingState");
break;
case QAbstractSocket::ConnectedState:
ui.label_4->setText("ConnectedState");
break;
case QAbstractSocket::BoundState:
ui.label_4->setText("BoundState");
break;
case QAbstractSocket::ListeningState:
ui.label_4->setText("ListeningState");
break;
case QAbstractSocket::ClosingState:
ui.label_4->setText("ClosingState");
break;
default:
break;
}
}
void QtWidgetsApplicationUDPZB::onSocketReadyRead()
{
while (m_udpSocket->hasPendingDatagrams())
{
QByteArray _datagram;
_datagram.resize(m_udpSocket->pendingDatagramSize());
QHostAddress _peerAddr;
quint16 _peerPort;
m_udpSocket->readDatagram(_datagram.data(), _datagram.size(), &_peerAddr, &_peerPort);
QString _str = _datagram.data();
QString _peer = "[From " + _peerAddr.toString() + ":" + QString::number(_peerPort) + "] ";
ui.plainTextEdit->appendPlainText(_peer + _str);
}
}
void QtWidgetsApplicationUDPZB::on_clearText_clicked()
{
ui.plainTextEdit->clear();
}
void QtWidgetsApplicationUDPZB::on_exit_clicked()
{
exit(0);
}
void QtWidgetsApplicationUDPZB::on_actStart_clicked()
{
QString ip = ui.lineEdit->text();
groupAdress = QHostAddress(ip);
quint16 groupPort = ui.spinBox->value();
if (m_udpSocket->bind(QHostAddress::AnyIPv4, groupPort, QUdpSocket::ShareAddress))
{
m_udpSocket->joinMulticastGroup(groupAdress);
ui.plainTextEdit->appendPlainText(u8"加入多播组");
ui.actStart->setEnabled(false);
ui.actStop->setEnabled(true);
}
else
{
ui.plainTextEdit->appendPlainText(u8"加入多播组失败");
}
}
void QtWidgetsApplicationUDPZB::on_actStop_clicked()
{
m_udpSocket->leaveMulticastGroup(groupAdress);
m_udpSocket->abort();
ui.actStart->setEnabled(true);
ui.actStop->setEnabled(false);
ui.plainTextEdit->appendPlainText(u8"已解除绑定");
}
void QtWidgetsApplicationUDPZB::on_btnSend_clicked()
{
quint16 _targetPort = ui.spinBox->value();
QString _msg = ui.lineEdit_2->text();
QByteArray _str = _msg.toUtf8();
m_udpSocket->writeDatagram(_str, groupAdress, _targetPort);
ui.plainTextEdit->appendPlainText("[OUT] " + _msg);
ui.lineEdit_2->clear();
ui.lineEdit_2->setFocus();
}
m_udpSocket->setSocketOption(QAbstractSocket::MulticastTtlOption, 1);设置缺省值为1,指的是UDP组播的数据报生存周期,跨过一个路由器,就会减去1,默认值为1,则代表只在同一个路由器下的局域网传播。
m_udpSocket->bind(QHostAddress::AnyIPv4, groupPort, QUdpSocket::ShareAddress)绑定的地址为QHostAddress::AnyIPv4,端口是多播组统一端口。