點擊上方“Qt學視覺”,選擇“星標”公眾號重磅干貨,第一時間送達
想要學習的同學們還請認真閱讀每篇文章,相信你一定會有所收獲
UDP通信概述
?? ? UDP(UserDatagramProtocol,用戶數據報協議)是輕量的、不可靠的、面向數據報(datagram)、無連接的協議,它可以用于對可靠性要求不高的場合與TCP通信不同,兩個程序之間進行UDP通信無 需預先建立持久的socket連接,UDP每次發送數據報都需要指定目標地址和端口(如下圖所示)。?
?? ?QUdpSocket類用于實現UDP通信,它從QAbstractSocket類繼承,因而與QTcpSocket共享 大部分的接口函數。主要區別是QUdpSocket以數據報傳輸數據,而不是以連續的數據流。發送數 據報使用函數QUdpSocket::writeDatagram(),數據報的長度一般少于512字節,每個數據報包含發 送者和接收者的IP地址和端口等信息。?
?? ?要進行UDP數據接收,要用QUdpSocket::bind()函數先綁定一個端口,用于接收傳入的數據 報。當有數據報傳入時會發射readyRead()信號,使用readDatagram()函數來讀取接收到的數據報。UDP消息傳送有單播、廣播、組播三種模式
單播(unicast)模式:一個UDP客戶端發出的數據報只發送到另一個指定地址和端口的 UDP客戶端,是一對一的數據傳輸。
廣播(broadcast)模式:一個UDP客戶端發出的數據報,在同一網絡范圍內其他所有的UDP客戶端都可以收到。QUdpSocket支持IPv4廣播。廣播經常用于實現網絡發現的協議。要獲取廣播數據只需在數據報中指定接收端地址為QHostAddres::Broadcast, —般的廣播 地址是 255.255.255.255。
組播(multicast)模式:也稱為多播。UDP客戶端加入到另一個組播IP地址指定的多播 組,成員向組播地址發送的數據報組內成員都可以接收到,類似于QQ群的功能. QUdpSocket::joinMulticastGroup()函數實現加入多播組的功能,加入多播組后,UDP數據 的收發與正常的UDP數據收發方法一樣。
?? ?使用廣播和多播模式,UDP可以實現一些比較靈活的通信功能,而TCP通信只有單播模式, 沒有廣播和多播模式。所以,UDP通信雖然不能保證數據傳輸的準確性,但是具有靈活性,一般 的即時通信軟件都是基于UDP通信的。?
?? ?QUdpSocket類從QAbstractSocket繼承而來,但是又定義了較多新的功能函數用于實現UDP 特有的一些功能,如數據報讀寫和多播通信功能。QUdpSocket沒有定義新的信號。QUdpSocket 的主要功能函數見表
bool bind(quint 16 port = 0)
為UDP通信綁定一個端口
qint64 writeDatagram(QByteArray &datagram, QHostAddress &host, quint 16 port)
向目標地址和端口的UDP客戶端發送數據報,返回成功 發送的字節數
bool hasPendingDatagrams()
當至少有一個數據報需要讀取時,返回true
qint64 pendingDatagramSize()
返回第一個待讀取的數據報的大小
qint64 readDatagram(char *data, qint64 maxSize)
讀取一個數據報,返回成功讀取的數據報的字節數
bool joinMulticastGroup(QHostAddress &groupAddress)
加入一個多播組
bool leaveMulticastGroup(QHostAddress &groupAddress)? ? ?
離開一個多播組
?? ?在單播、廣播和多播模式下,UDP程序都是對等的,不像TCP通信那樣分為客戶端和服務器 端。多播和廣播的實現方式基本相同,只是數據報的目標IP地址設置不同,多播模式需要加入多 播組,實現方式有較大差異。
例程如下
頭文件
#pragma once#include #include "ui_QGuiUdpClient.h"#include #include class QGuiUdpClient : public QMainWindow{ Q_OBJECTpublic: QGuiUdpClient(QWidget *parent = Q_NULLPTR); ~QGuiUdpClient();private slots: //自定義槽函數 void onSocketStateChange(QAbstractSocket::SocketState socketState); void onSocketReadyRead();//讀取socket傳入的數據 void actStart_triggered(); void actStop_triggered(); void actHostInfo_triggered(); void actClear_triggered(); void btnSend_clicked(); void btnBroadcast_clicked();private: Ui::QGuiUdpClient ui;private: QLabel* m_pLabSocketState;//socket狀態顯示標簽 QUdpSocket* m_pUdpSocket;// QString getLocalIP();//獲取本機IP地址};
源文件
#include "QGuiUdpClient.h"#include #pragma execution_character_set("utf-8")QGuiUdpClient::QGuiUdpClient(QWidget *parent) : QMainWindow(parent){ ui.setupUi(this); m_pLabSocketState = new QLabel("Socket狀態:");// m_pLabSocketState->setMinimumWidth(200); ui.statusBar->addWidget(m_pLabSocketState); QString localIP = getLocalIP();//本機IP this->setWindowTitle(this->windowTitle() + "----本機IP:" + localIP); ui.comboTargetIP->addItem(localIP); m_pUdpSocket = new QUdpSocket(this);//用于與連接的客戶端通訊的QTcpSocket connect(m_pUdpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChange(QAbstractSocket::SocketState))); onSocketStateChange(m_pUdpSocket->state()); connect(m_pUdpSocket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead())); connect(ui.actStart, SIGNAL(triggered()), this, SLOT(actStart_triggered())); connect(ui.actStop, SIGNAL(triggered()), this, SLOT(actStop_triggered())); connect(ui.actHostInfo, SIGNAL(triggered()), this, SLOT(actHostInfo_triggered())); connect(ui.actClear, SIGNAL(triggered()), this, SLOT(actClear_triggered())); connect(ui.btnSend, SIGNAL(clicked()), this, SLOT(btnSend_clicked())); connect(ui.btnBroadcast, SIGNAL(clicked()), this, SLOT(btnBroadcast_clicked()));}QGuiUdpClient::~QGuiUdpClient(){ m_pUdpSocket->abort(); delete m_pUdpSocket;}void QGuiUdpClient::onSocketStateChange(QAbstractSocket::SocketState socketState){ switch (socketState) { case QAbstractSocket::UnconnectedState: m_pLabSocketState->setText("scoket狀態:UnconnectedState"); break; case QAbstractSocket::HostLookupState: m_pLabSocketState->setText("scoket狀態:HostLookupState"); break; case QAbstractSocket::ConnectingState: m_pLabSocketState->setText("scoket狀態:ConnectingState"); break; case QAbstractSocket::ConnectedState: m_pLabSocketState->setText("scoket狀態:ConnectedState"); break; case QAbstractSocket::BoundState: m_pLabSocketState->setText("scoket狀態:BoundState"); break; case QAbstractSocket::ClosingState: m_pLabSocketState->setText("scoket狀態:ClosingState"); break; case QAbstractSocket::ListeningState: m_pLabSocketState->setText("scoket狀態:ListeningState"); break; }}void QGuiUdpClient::onSocketReadyRead(){ while (m_pUdpSocket->hasPendingDatagrams()) { QByteArray datagram; datagram.resize(m_pUdpSocket->pendingDatagramSize()); QHostAddress peerAddr; quint16 peerPort; m_pUdpSocket->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 QGuiUdpClient::actStart_triggered(){ quint16 port = ui.spinBindPort->value(); //本機UDP端口 if (m_pUdpSocket->bind(port))//綁定端口成功 { ui.plainTextEdit->appendPlainText("**已成功綁定"); ui.plainTextEdit->appendPlainText("**綁定端口:" + QString::number(m_pUdpSocket->localPort())); ui.actStart->setEnabled(false); ui.actStop->setEnabled(true); } else ui.plainTextEdit->appendPlainText("**綁定失敗");}void QGuiUdpClient::actStop_triggered(){ m_pUdpSocket->abort(); //不能解除綁定 ui.actStart->setEnabled(true); ui.actStop->setEnabled(false); ui.plainTextEdit->appendPlainText("**已解除綁定");}void QGuiUdpClient::actHostInfo_triggered(){ QString hostName = QHostInfo::localHostName();//本地主機名 ui.plainTextEdit->appendPlainText("本機主機名:" + hostName + "\n"); QHostInfo hostInfo = QHostInfo::fromName(hostName); QList addList = hostInfo.addresses();// if (!addList.isEmpty()) { for (int i = 0; i < addList.count(); i++) { QHostAddress aHost = addList.at(i); if (QAbstractSocket::IPv4Protocol == aHost.protocol()) { QString IP = aHost.toString(); ui.plainTextEdit->appendPlainText("本機IP地址:" + aHost.toString()); if (ui.comboTargetIP->findText(IP) < 0) ui.comboTargetIP->addItem(IP); } } }}void QGuiUdpClient::actClear_triggered(){ ui.plainTextEdit->clear();}void QGuiUdpClient::btnSend_clicked(){ QString targetIP = ui.comboTargetIP->currentText(); //目標IP QHostAddress targetAddr(targetIP); quint16 targetPort = ui.spinTargetPort->value();//目標port QString msg = ui.editMsg->text();//發送的消息內容 QByteArray str = msg.toUtf8(); m_pUdpSocket->writeDatagram(str, targetAddr, targetPort); //發出數據報 ui.plainTextEdit->appendPlainText("[out] " + msg); ui.editMsg->clear(); ui.editMsg->setFocus();}void QGuiUdpClient::btnBroadcast_clicked(){ quint16 targetPort = ui.spinTargetPort->value(); //目標端口 QString msg = ui.editMsg->text(); QByteArray str = msg.toUtf8(); m_pUdpSocket->writeDatagram(str, QHostAddress::Broadcast, targetPort); ui.plainTextEdit->appendPlainText("[broadcast] " + msg); ui.editMsg->clear(); ui.editMsg->setFocus();}QString QGuiUdpClient::getLocalIP(){ QString hostName = QHostInfo::localHostName();//本地主機名 QHostInfo hostInfo = QHostInfo::fromName(hostName); QString localIP = ""; QList addList = hostInfo.addresses();// if (!addList.isEmpty()) { for (int i = 0; i < addList.count(); i++) { QHostAddress aHost = addList.at(i); if (QAbstractSocket::IPv4Protocol == aHost.protocol()) { localIP = aHost.toString(); break; } } } return localIP;}
頭文件
#pragma once#include #include "ui_QGuiUdpServer.h"#include #include class QGuiUdpServer : public QMainWindow{ Q_OBJECTpublic: QGuiUdpServer(QWidget *parent = Q_NULLPTR); ~QGuiUdpServer();private slots: //自定義槽函數 void onSocketStateChange(QAbstractSocket::SocketState socketState); void onSocketReadyRead();//讀取socket傳入的數據 void actStart_triggered(); void actStop_triggered(); void actHostInfo_triggered(); void actClear_triggered(); void btnSend_clicked(); void btnBroadcast_clicked();private: Ui::QGuiUdpServer ui;private: QLabel* m_pLabSocketState;//socket狀態顯示標簽 QUdpSocket* m_pUdpSocket;// QString getLocalIP();//獲取本機IP地址};
源文件
#include "QGuiUdpServer.h"#include #pragma execution_character_set("utf-8")QGuiUdpServer::QGuiUdpServer(QWidget *parent) : QMainWindow(parent){ ui.setupUi(this); m_pLabSocketState = new QLabel("Socket狀態:");// m_pLabSocketState->setMinimumWidth(200); ui.statusBar->addWidget(m_pLabSocketState); QString localIP = getLocalIP();//本機IP this->setWindowTitle(this->windowTitle() + "----本機IP:" + localIP); ui.comboTargetIP->addItem(localIP); m_pUdpSocket = new QUdpSocket(this);//用于與連接的客戶端通訊的QTcpSocket connect(m_pUdpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(onSocketStateChange(QAbstractSocket::SocketState))); onSocketStateChange(m_pUdpSocket->state()); connect(m_pUdpSocket, SIGNAL(readyRead()), this, SLOT(onSocketReadyRead())); connect(ui.actStart, SIGNAL(triggered()), this, SLOT(actStart_triggered())); connect(ui.actStop, SIGNAL(triggered()), this, SLOT(actStop_triggered())); connect(ui.actHostInfo, SIGNAL(triggered()), this, SLOT(actHostInfo_triggered())); connect(ui.actClear, SIGNAL(triggered()), this, SLOT(actClear_triggered())); connect(ui.btnSend, SIGNAL(clicked()), this, SLOT(btnSend_clicked())); connect(ui.btnBroadcast, SIGNAL(clicked()), this, SLOT(btnBroadcast_clicked()));}QGuiUdpServer::~QGuiUdpServer(){ m_pUdpSocket->abort(); delete m_pUdpSocket;}void QGuiUdpServer::onSocketStateChange(QAbstractSocket::SocketState socketState){ switch (socketState) { case QAbstractSocket::UnconnectedState: m_pLabSocketState->setText("scoket狀態:UnconnectedState"); break; case QAbstractSocket::HostLookupState: m_pLabSocketState->setText("scoket狀態:HostLookupState"); break; case QAbstractSocket::ConnectingState: m_pLabSocketState->setText("scoket狀態:ConnectingState"); break; case QAbstractSocket::ConnectedState: m_pLabSocketState->setText("scoket狀態:ConnectedState"); break; case QAbstractSocket::BoundState: m_pLabSocketState->setText("scoket狀態:BoundState"); break; case QAbstractSocket::ClosingState: m_pLabSocketState->setText("scoket狀態:ClosingState"); break; case QAbstractSocket::ListeningState: m_pLabSocketState->setText("scoket狀態:ListeningState"); break; }}void QGuiUdpServer::onSocketReadyRead(){ while (m_pUdpSocket->hasPendingDatagrams()) { QByteArray datagram; datagram.resize(m_pUdpSocket->pendingDatagramSize()); QHostAddress peerAddr; quint16 peerPort; m_pUdpSocket->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 QGuiUdpServer::actStart_triggered(){ quint16 port = ui.spinBindPort->value(); //本機UDP端口 if (m_pUdpSocket->bind(port))//綁定端口成功 { ui.plainTextEdit->appendPlainText("**已成功綁定"); ui.plainTextEdit->appendPlainText("**綁定端口:" + QString::number(m_pUdpSocket->localPort())); ui.actStart->setEnabled(false); ui.actStop->setEnabled(true); } else ui.plainTextEdit->appendPlainText("**綁定失敗");}void QGuiUdpServer::actStop_triggered(){ m_pUdpSocket->abort(); //不能解除綁定 ui.actStart->setEnabled(true); ui.actStop->setEnabled(false); ui.plainTextEdit->appendPlainText("**已解除綁定");}void QGuiUdpServer::actHostInfo_triggered(){ QString hostName = QHostInfo::localHostName();//本地主機名 ui.plainTextEdit->appendPlainText("本機主機名:" + hostName + "\n"); QHostInfo hostInfo = QHostInfo::fromName(hostName); QList addList = hostInfo.addresses();// if (!addList.isEmpty()) { for (int i = 0; i < addList.count(); i++) { QHostAddress aHost = addList.at(i); if (QAbstractSocket::IPv4Protocol == aHost.protocol()) { QString IP = aHost.toString(); ui.plainTextEdit->appendPlainText("本機IP地址:" + aHost.toString()); if (ui.comboTargetIP->findText(IP) < 0) ui.comboTargetIP->addItem(IP); } } }}void QGuiUdpServer::actClear_triggered(){ ui.plainTextEdit->clear();}void QGuiUdpServer::btnSend_clicked(){ QString targetIP = ui.comboTargetIP->currentText(); //目標IP QHostAddress targetAddr(targetIP); quint16 targetPort = ui.spinTargetPort->value();//目標port QString msg = ui.editMsg->text();//發送的消息內容 QByteArray str = msg.toUtf8(); m_pUdpSocket->writeDatagram(str, targetAddr, targetPort); //發出數據報 ui.plainTextEdit->appendPlainText("[out] " + msg); ui.editMsg->clear(); ui.editMsg->setFocus();}void QGuiUdpServer::btnBroadcast_clicked(){ quint16 targetPort = ui.spinTargetPort->value(); //目標端口 QString msg = ui.editMsg->text(); QByteArray str = msg.toUtf8(); m_pUdpSocket->writeDatagram(str, QHostAddress::Broadcast, targetPort); ui.plainTextEdit->appendPlainText("[broadcast] " + msg); ui.editMsg->clear(); ui.editMsg->setFocus();}QString QGuiUdpServer::getLocalIP(){ QString hostName = QHostInfo::localHostName();//本地主機名 QHostInfo hostInfo = QHostInfo::fromName(hostName); QString localIP = ""; QList addList = hostInfo.addresses();// if (!addList.isEmpty()) { for (int i = 0; i < addList.count(); i++) { QHostAddress aHost = addList.at(i); if (QAbstractSocket::IPv4Protocol == aHost.protocol()) { localIP = aHost.toString(); break; } } } return localIP;}