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