Qt之UDP通信
目錄
一、UDP簡介
二、QUdpSocket類
三、UDP服務器
四、UDP客戶端
五、代碼
1.udp服務端
2.udp客戶端
一、UDP簡介
UDP(User Datagram Protocol 即用戶數據報協議)是一個輕量級的,不可靠的,面向數據
報的無連接協議。由于 UDP 的特性:它不屬于連接型協議,因而具有資源消耗小,處理速度快的優點,所以通常音頻、視頻和普通數據在傳送時使用 UDP 較多,因為它們即使偶爾丟失一兩個數據包,也不會對接收結果產生太大影響。
UDP 通信示意圖如下:
UDP 消息傳送有三種模式,分別是單播、廣播和組播三種模式。
①單播(unicast): 單播用于兩個主機之間的端對端通信,需要知道對方的 IP 地址與端口
②廣播(broadcast): 廣播 UDP 與單播 UDP 的區別就是 IP 地址不同,廣播一般使用廣播地址
255.255.255.255,將消息發送到在同一廣播(也就是局域網內同一網段) 網絡上的每個主機
注意:本地廣播信息是不會被路由器轉發,所以如果一個服務端在win,另外一個客戶端在虛擬機說,這時就需要配置虛擬機的端口轉發,這樣虛擬機才會連得上服務器。
③組播(multicast): 組播(多點廣播),也稱為多播,將網絡中同一業務類型主機進行了邏輯上的分組,進行數據收發的時候其數據僅僅在同一分組中進行,其他的主機沒有加入此分組不能收發對應的數據。
在廣域網上廣播的時候,其中的交換機和路由器只向需要獲取數據的主機復制并轉發數據。主機可以向路由器請求加入或退出某個組,網絡中的路由器和交換機有選擇地復制并傳輸數據,將數據僅僅傳輸給組內的主機。多播的這種功能,可以一次將數據發送到多個主機,又能保證不影響其他不需要(未加入組)的主機的其他通信。
注意: 單播一樣和多播是允許在廣域網即 Internet 上進行傳輸的,而廣播僅僅在同一局域網上才能進行。
二、QUdpSocket類
QT 的 socket 類之間的關系:?
QUdpSocket 類提供了一個 UDP 套接字。 QUdpSocket 是 QAbstractSocket 的子類,允許發
送和接收 UDP 數據報。
常用API函數
①構造函數
QUdpSocket::QUdpSocket(QObject *parent = Q_NULLPTR)
②如果至少有一個數據報在等待被讀取,則返回true,否則返回false。
bool QUdpSocket::hasPendingDatagrams() const?
③服務器綁定端口
bool bind(const QHostAddress &address, quint16 port = 0, BindMode mode = DefaultForPlatform);
④返回第一個待處理的UDP數據報的大小Byte。如果沒有可用的數據報,該函數返回-1。
qint64 QUdpSocket::pendingDatagramSize() const
⑤接收數據
qint64 QUdpSocket::readDatagram(char *data, qint64 maxSize, QHostAddress *address = Q_NULLPTR, quint16 *port = Q_NULLPTR)
接收一個不大于maxSize字節的數據報并將其存儲在data中。發送者的主機地址和端口存儲在*address和*port中(除非指針為0)。成功時返回數據報的大小;否則返回-1。
如果maxSize太小,數據報的其余部分將被丟失。為了避免數據丟失,在試圖讀取數據報之前,應調用pendingDatagramSize()來確定未決數據報的大小。如果maxSize為0,數據報將被丟棄。
?
⑥發送數據
qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port)
將數據報以大小的方式發送到端口端口的主機地址。成功時返回發送的字節數,否則返回-1。
數據報總是被寫成一個塊。數據報的最大尺寸與平臺高度相關,但可以低至8192字節。如果數據報太大,這個函數將返回-1,error()將返回DatagramTooLargeError。
一般來說,發送大于512字節的數據報是不利的,因為即使它們被成功發送,在到達最終目的地之前,它們很可能被IP層分割開來。
?三、UDP服務器
?1.創建QUdpSocket對象
mSocket = new QUdpSocket(this);
②綁定地址和端口號
msocket->bind(ip,端口號);
③收到數據時,會觸發readyRead()信號,自定義readPendingDatagrams()進行讀取數據;
connect(msocket,&QUdpSocket::readyRead,?this,&Widget::readPendingDatagrams);
④在while循環中讀取數據,只要有數據,就一直讀取并處理。
? void Server::readPendingDatagrams()
? {
? ? ? while (udpSocket->hasPendingDatagrams()) //數據報等待被讀取
???????{
? ? ? ? ?????????//數據緩沖區
????????????????QByteArray arr;
????????????????//調整緩沖區的大小和收到的數據大小一致 ????????????????
????????????????arr.resize(mSocket->bytesAvailable()); //接收數據
????????????????mSocket->readDatagram(arr.data(),arr.size(),&addr,&port);
? ? ? ? ? ? ? ? //將arr.data轉為字符串即可
????????????????QString str = arr.data();
? ? ? }
? }
通信(先接收) 收到數據會觸發信號readyRead, 通過QUdpSocket對象的readDatagram函數來接收數據 。
readyRead()信號在數據報到達時發出。在這種情況下, hasPendingDatagrams()返回 true。調用 pendingDatagramSize()來獲取第一個待處理數據報的大小,并調用 readDatagram()接收數據。
注意:當接收到readyRead()信號時,一個傳入的數據報應該被讀取,否則這個信號將不會被發送到下一個數據報。
⑤發送數據
qint64 QUdpSocket::writeDatagram(const char *data, qint64 size, const QHostAddress &address, quint16 port)
若是廣播消息,與單播消息不同的是將目標 IP 地址換成了廣播地址,一般廣播地址為 255.255.255.255,也可以使用QHostAddress::Broadcast獲取廣播地址。
QHostAddress peerAddr = QHostAddress::Broadcast;
只需要將客戶端發送數據:writeDatagram的IP地址改為廣播地址即可。
四、UDP客戶端
①創建QUdpSocket對象
mSocket = new QUdpSocket(this);
②發送數據到指定的地址和端口號
writeDatagram(數據,接收方ip,接收方端口號);
?發送的數據要是QByteArray類型,Qt中將字符串轉為QByteArray可以使用.toUtf8函數。
五、代碼
1.udp服務端
?頭文件
#ifndef UDPSERVER_H #define UDPSERVER_H#include <QWidget> #include <QtNetwork>QT_BEGIN_NAMESPACE namespace Ui { class UdpServer; } QT_END_NAMESPACEclass UdpServer : public QWidget {Q_OBJECTpublic:UdpServer(QWidget *parent = nullptr);~UdpServer();private slots:void on_pushButton_start_clicked();void on_pushButton_send_clicked();void readPendingDatagrams();private:Ui::UdpServer *ui;//Udp服務器QUdpSocket *mSocket;//通信的ip和端口,用于獲取發送者的 IP 和端口QHostAddress addr;quint16 port; }; #endif // UDPSERVER_H源文件
#include "udpserver.h" #include "ui_udpserver.h"UdpServer::UdpServer(QWidget *parent): QWidget(parent), ui(new Ui::UdpServer) {ui->setupUi(this); }UdpServer::~UdpServer() {delete ui; }//啟動 void UdpServer::on_pushButton_start_clicked() {//1.創建QUdpSocket對象mSocket = new QUdpSocket(this);//2.連接接收數據信號和槽QObject::connect(mSocket,&QUdpSocket::readyRead,this,&UdpServer::readPendingDatagrams);//3.綁定mSocket->bind(QHostAddress::Any,ui->spinBox->value());//連接回車發送的信號和槽QObject::connect(ui->lineEdit,&QLineEdit::returnPressed,this,&UdpServer::on_pushButton_send_clicked);//禁止端口號和啟動按鈕ui->spinBox->setEnabled(false);ui->pushButton_start->setEnabled(false); }void UdpServer::on_pushButton_send_clicked() {//獲取發送的數據QByteArray arr = ui->lineEdit->text().toUtf8();//發送mSocket->writeDatagram(arr,addr,port);//顯示發送的內容ui->textBrowser->insertPlainText("send:"+QString(arr)+"\n");//情況lineEditui->lineEdit->clear(); }void UdpServer::readPendingDatagrams() {//數據緩沖區QByteArray arr;while(mSocket->hasPendingDatagrams()){//調整緩沖區的大小和收到的數據大小一致arr.resize(mSocket->bytesAvailable());//接收數據mSocket->readDatagram(arr.data(),arr.size(),&addr,&port);//顯示ui->textBrowser->insertPlainText(addr.toString()+":"+QString(arr)+"\n");//使能發送按鈕和編輯框ui->lineEdit->setEnabled(true);ui->pushButton_send->setEnabled(true);} }2.udp客戶端
頭文件
#ifndef UDPCILENT_H #define UDPCILENT_H#include <QWidget> #include <QtNetwork>QT_BEGIN_NAMESPACE namespace Ui { class UdpCilent; } QT_END_NAMESPACEclass UdpCilent : public QWidget {Q_OBJECTpublic:UdpCilent(QWidget *parent = nullptr);~UdpCilent();private slots:void on_pushButton_send_clicked();void readPendingDatagrams();private:Ui::UdpCilent *ui;//UDP客戶端QUdpSocket *mSocket; }; #endif // UDPCILENT_H?源文件
#include "udpcilent.h" #include "ui_udpcilent.h"UdpCilent::UdpCilent(QWidget *parent): QWidget(parent), ui(new Ui::UdpCilent) {ui->setupUi(this);//1.創建QUdpSocketmSocket = new QUdpSocket(this);//2.通信(接收)QObject::connect(mSocket,&QUdpSocket::readyRead,this,&UdpCilent::readPendingDatagrams);//連接回車發送的信號和槽QObject::connect(ui->lineEdit_send,&QLineEdit::returnPressed,this,&UdpCilent::on_pushButton_send_clicked);}UdpCilent::~UdpCilent() {delete ui; }//發送 void UdpCilent::on_pushButton_send_clicked() {//獲取發送的數據QByteArray arr = ui->lineEdit_send->text().toUtf8();//發送//mSocket->writeDatagram(arr,QHostAddress(ui->lineEdit_ip->text()),ui->spinBox->value());mSocket->writeDatagram(arr,QHostAddress::Broadcast,ui->spinBox->value());//顯示發送的內容ui->textBrowser->insertPlainText("send:"+QString(arr)+"\n");//情況lineEditui->lineEdit_send->clear(); }void UdpCilent::readPendingDatagrams() {QHostAddress addr; //用于獲取發送者的 IP 和端口quint16 port;//數據緩沖區QByteArray arr;while(mSocket->hasPendingDatagrams()){//調整緩沖區的大小和收到的數據大小一致arr.resize(mSocket->bytesAvailable());//接收數據mSocket->readDatagram(arr.data(),arr.size(),&addr,&port);//顯示ui->textBrowser->insertPlainText(addr.toString()+":"+QString(arr)+"\n");} }結果:?
?
?
總結
- 上一篇: 武汉大学测绘学院19级导航工程第三学期专
- 下一篇: 怎么查看当前服务器的运行环境,如何查看A