Qt學習筆記(三十四):TCP 通信


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()
{
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章