QUdpSocket 類提供 UDP 套接字。
UDP(用戶數據報協議)是一種輕量級、不可靠、面向數據報、無連接的協議。它可以在可靠性不重要的情況下使用。QUdpSocket 是 QAbstractSocket 的一個子類,它允許您發送和接收 UDP 數據報。
使用此類的最常見方法是使用 bind() 綁定到地址和端口,然後調用 writeDatagram() 和 readDatagram() 來傳輸數據。如果要使用標準的 QIODevice 函數 read()、readLine()、write() 等,必須首先通過調用 connectToHost() 將套接字直接連接到對等方。
每次數據報寫入網絡時,套接字都會發出 bytesWritten() 信號。如果只想發送數據報,則不需要調用 bind()。
每當數據報到達時,就會發出 readyRead() 信號。在這種情況下,hasPendingDatagrams() 返回 true。調用pendingDatagramSize() 獲取第一個掛起數據報的大小,並使用 readDatagram() 讀取它。
注意:當您收到 readyRead() 信號時,應該讀取傳入的數據報,否則下一個數據報將不會發出此信號。
ui 界面:
客戶端和服務器端的代碼幾乎完全一樣,不一樣的只有 綁定和發送數據時指定的 ip 地址和端口不一樣,其他代碼都是一樣的;
客戶端代碼如下:
widget.h:
#include <QWidget>
#include <QUdpSocket>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_btnSend_clicked();
void on_btnClose_clicked();
private:
Ui::Widget *ui;
QUdpSocket *udpSocket;
};
widget.cpp:
#include "widget.h"
#include "ui_widget.h"
#include <QHostAddress>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
// 實例化套接字對象
udpSocket = new QUdpSocket(this);
// 綁定本機 ip 地址和端口(服務器端綁定的是 8888 端口)
udpSocket->bind(QHostAddress("127.0.0.1"), 6666);
// 當接收到數據報時觸發 readRead 信號
connect(udpSocket, &QUdpSocket::readyRead, [=](){
// 獲取第一個掛起的 UDP 數據報的大小。如果沒有可用的數據報,則此函數返回 -1。
qint64 len = udpSocket->pendingDatagramSize();
// 讀取數據報:參數1 表示存儲數據報的緩存;參數2 表示最大字節數;
// 成功返回實際讀取到的數據報的大小;否則返回 -1。
// 如果 參數2 太小,則數據報的其餘部分將丟失。爲了避免數據丟失,在嘗試讀取之前,
// 調用 pendingDatagramSize() 來確定掛起的數據報的大小。如果 參數2 爲 0,則該數據報將被丟棄。
char buf[len] = {0};
QHostAddress clientIP; // 保存客戶端的 ip地址
quint16 port; // 保存客戶端的 端口號
qint64 size = udpSocket->readDatagram(buf, len, &clientIP, &port);
if (size > 0)
{
// 將 char* 轉換成 QString
// 不知道爲什麼我讀取到的 buf 最後有時候會有亂碼;
// 使用下面這種方法截取數據並轉換成字符串,可以去掉後面的亂碼。
QString str = QString::fromUtf8(buf, size);
// 如果讀取到數據報,則顯示到界面上
QString data = QString("[%1:%2] %3").arg(clientIP.toString()).arg(port).arg(str);
ui->textEdit_Receive->append(data);
}
});
}
Widget::~Widget()
{
delete ui;
}
// 發送數據
void Widget::on_btnSend_clicked()
{
// 獲取對方的 ip 和端口
QString ip = ui->lineEdit_IP->text();
qint16 port = ui->lineEdit_Port->text().toInt();
// 獲取數據
QString text = ui->textEdit_Send->toPlainText();
// 發送數據報:成功返回實際發送的數據報字節數,否則返回 -1;
// 數據報總是作爲一個塊寫入。數據報的最大大小高度依賴於平臺,但可以低至 8192 字節。
// 如果數據報太大,此函數將返回 -1,error() 將返回 DatagramTooLargeError。
// 一般情況下,發送大於 512 字節的數據報是不可取的,因爲即使它們發送成功,
// 它們也可能在到達最終目的地之前被 IP 層分段。
// 警告:在已連接的 UDP 套接字上調用此函數可能會導致錯誤,並且不會發送任何數據包。
// 如果使用的是已連接的套接字,請使用 write() 發送數據報。
// 注意:writeDatagram() 方法中的 ip 地址和端口是目標主機的;要和 bind() 方法中的地址和端口區分開;
udpSocket->writeDatagram(text.toUtf8(), QHostAddress(ip), port);
}
// 斷開連接(因爲沒有使用 connectToHost() 方法連接到服務器,所以不需要斷開連接)
void Widget::on_btnClose_clicked()
{
}