目錄
一、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)的無(wú)連接協(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ī)說(shuō),這時(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ī)沒(méi)有加入此分組不能收發(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ù)?,而廣播僅僅在同一局域網(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。如果沒(méi)有可用的數(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)的大?。环駝t返回-1。
如果maxSize太小,數(shù)據(jù)報(bào)的其余部分將被丟失。為了避免數(shù)據(jù)丟失,在試圖讀取數(shù)據(jù)報(bào)之前,應(yīng)調(diào)用pendingDatagramSize()來(lái)確定未決數(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)總是被寫(xiě)成一個(gè)塊。數(shù)據(jù)報(bào)的最大尺寸與平臺(tái)高度相關(guān),但可以低至8192字節(jié)。如果數(shù)據(jù)報(bào)太大,這個(gè)函數(shù)將返回-1,error()將返回DatagramTooLargeError。
一般來(lái)說(shuō),發(fā)送大于512字節(jié)的數(shù)據(jù)報(bào)是不利的,因?yàn)榧词顾鼈儽怀晒Πl(fā)送,在到達(dá)最終目的地之前,它們很可能被IP層分割開(kāi)來(lái)。
?三、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, 通過(guò)QUdpSocket對(duì)象的readDatagram函數(shù)來(lái)接收數(shù)據(jù) 。
readyRead()信號(hào)在數(shù)據(jù)報(bào)到達(dá)時(shí)發(fā)出。在這種情況下, hasPendingDatagrams()返回 true。調(diào)用 pendingDatagramSize()來(lái)獲取第一個(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_NAMESPACE
class UdpServer : public QWidget
{
Q_OBJECT
public:
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());
//連接回車(chē)發(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");
//情況lineEdit
ui->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_NAMESPACE
class UdpCilent : public QWidget
{
Q_OBJECT
public:
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)建QUdpSocket
mSocket = new QUdpSocket(this);
//2.通信(接收)
QObject::connect(mSocket,&QUdpSocket::readyRead,this,&UdpCilent::readPendingDatagrams);
//連接回車(chē)發(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");
//情況lineEdit
ui->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é)果:?
?文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-786018.html
?文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-786018.html
到了這里,關(guān)于Qt之UDP通信的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!