QT網絡通信的接口與使用


文章目錄

  • 前言
  • 1.服務端實現流程
    • 1.1步驟 1:創建 QTcpServer 并監聽端口
    • 1.2步驟 2:處理新連接請求
    • 1.3步驟 3:接收客戶端數據
    • 1.4步驟 4:處理客戶端斷開
  • 2.客戶端實現流程
    • 2.1步驟 1:創建 QTcpSocket 并連接服務器
    • 2.2步驟 2:發送數據
    • 2.3步驟 3:接收服務器回復
    • 2.4步驟 4:處理連接和錯誤
  • 3.關鍵注意事項
  • 4.TCP粘包問題及其處理
    • 4.1TCP粘包是什么
    • 4.2TCP粘包為什么會產生
    • 4.3TCP粘包的解決方案


前言

在Qt中實現TCP通信主要依賴 QTcpServer(服務端)和 QTcpSocket(客戶端和服務端通信)類。

TCP/IP通信(即SOCKET通信)是通過網線將服務器Server端和客戶機Client端進行連接,在遵循ISO/OSI模型的四層層級構架的基礎上通過TCP/IP協議建立的通訊。控制器可以設置為服務器端或客戶端。

服務端(簡化版)

class MyServer : public QObject {Q_OBJECT
public:MyServer(QObject *parent = nullptr) : QObject(parent) {server = new QTcpServer(this);connect(server, &QTcpServer::newConnection, this, &MyServer::onNewConnection);server->listen(QHostAddress::Any, 8888);}private slots:void onNewConnection() { /* ... */ }void onReadyRead() { /* ... */ }void onDisconnected() { /* ... */ }private:QTcpServer *server;QList<QTcpSocket*> m_clients;
};

客戶端(簡化版)

class MyClient : public QObject {Q_OBJECT
public:MyClient(QObject *parent = nullptr) : QObject(parent) {socket = new QTcpSocket(this);connect(socket, &QTcpSocket::connected, this, &MyClient::onConnected);connect(socket, &QTcpSocket::readyRead, this, &MyClient::onReadyRead);socket->connectToHost("127.0.0.1", 8888);}void send(const QString &message) {socket->write(message.toUtf8());}private slots:void onConnected() { /* ... */ }void onReadyRead() { /* ... */ }private:QTcpSocket *socket;
};

運行效果
服務端啟動后監聽端口,客戶端連接并發送數據。
服務端接收數據并回復,客戶端顯示回復內容。
斷開連接后資源自動釋放。

1.服務端實現流程

1.1步驟 1:創建 QTcpServer 并監聽端口

// 創建TCP服務端對象
QTcpServer *server = new QTcpServer(this);// 監聽所有IP的指定端口(例如8888)
if (!server->listen(QHostAddress::Any, 8888)) {qDebug() << "Server could not start. Error:" << server->errorString();
} else {qDebug() << "Server started on port 8888";
}

1.2步驟 2:處理新連接請求

當客戶端連接時,QTcpServer 會觸發 newConnection 信號,需通過槽函數處理:

// 連接信號到槽函數
connect(server, &QTcpServer::newConnection, this, &MyServer::onNewConnection);// 槽函數實現
void MyServer::onNewConnection() {// 獲取新連接的socket對象QTcpSocket *socket = server->nextPendingConnection();// 存儲socket以便后續通信(例如添加到列表)m_clients.append(socket);// 處理客戶端數據到達的信號connect(socket, &QTcpSocket::readyRead, this, &MyServer::onReadyRead);// 處理斷開連接的信號connect(socket, &QTcpSocket::disconnected, this, &MyServer::onDisconnected);
}

1.3步驟 3:接收客戶端數據

通過 readyRead 信號讀取數據:

void MyServer::onReadyRead() {QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());if (!socket) return;QByteArray data = socket->readAll();qDebug() << "Received data:" << data;// 示例:回復客戶端socket->write("Server received: " + data);
}

1.4步驟 4:處理客戶端斷開

void MyServer::onDisconnected() {QTcpSocket *socket = qobject_cast<QTcpSocket*>(sender());if (!socket) return;m_clients.removeOne(socket);socket->deleteLater();qDebug() << "Client disconnected";
}

2.客戶端實現流程

2.1步驟 1:創建 QTcpSocket 并連接服務器

QTcpSocket *socket = new QTcpSocket(this);// 連接服務器(假設服務器IP為127.0.0.1,端口8888)
socket->connectToHost("127.0.0.1", 8888);// 監聽連接成功信號
connect(socket, &QTcpSocket::connected, this, &MyClient::onConnected);// 監聽數據到達信號
connect(socket, &QTcpSocket::readyRead, this, &MyClient::onReadyRead);// 監聽錯誤信號
connect(socket, &QTcpSocket::errorOccurred, this, &MyClient::onError);

2.2步驟 2:發送數據

void MyClient::sendData(const QByteArray &data) {if (socket->state() == QAbstractSocket::ConnectedState) {socket->write(data);socket->flush(); // 確保立即發送}
}

2.3步驟 3:接收服務器回復

void MyClient::onReadyRead() {QByteArray data = socket->readAll();qDebug() << "Server response:" << data;
}

2.4步驟 4:處理連接和錯誤

void MyClient::onConnected() {qDebug() << "Connected to server!";
}void MyClient::onError(QAbstractSocket::SocketError error) {qDebug() << "Error:" << socket->errorString();
}

3.關鍵注意事項

  1. 異步通信:
    Qt的TCP操作基于事件循環,所有操作(連接、讀寫)都是異步的,需通過信號槽處理結果。

  2. 數據分包與粘包:
    TCP是流式協議,需自行處理數據邊界(例如定義協議頭尾或使用長度前綴)。

  3. 資源管理:
    及時釋放斷開連接的 QTcpSocket 對象(調用 deleteLater)。

  4. 跨線程操作:
    若在多線程中使用,需將 QTcpSocket 或 QTcpServer 移至子線程(使用 moveToThread)。

4.TCP粘包問題及其處理

4.1TCP粘包是什么

TCP的粘包和拆包問題往往出現在基于TCP協議的通訊中,比如RPC框架、Netty等。

TCP在接受數據的時候,有一個滑動窗口來控制接受數據的大小,這個滑動窗口你就可以理解為一個緩沖區的大小。緩沖區滿了就會把數據發送。數據包的大小是不固定的,有時候比緩沖區大有時候小。
如果一次請求發送的數據量比較小,沒達到緩沖區大小,TCP則會將多個請求合并為同一個請求進行發送,這就形成了粘包問題;
如果一次請求發送的數據量比較大,超過了緩沖區大小,TCP就會將其拆分為多次發送,這就是拆包,也就是將一個大的包拆分為多個小包進行發送。

這是最好理解的粘包問題的產生原因。還有一些其他的原因比如
1 ? 客戶端的發送頻率遠高于服務器的接收頻率,就會導致數據在服務器的tcp接收緩沖區滯留形成粘連,比如客戶端1s內連續發送了兩個hello world!,服務器過了2s才接收數據,那一次性讀出兩個hello world!。
2 ? tcp底層的安全和效率機制不允許字節數特別少的小包發送頻率過高,tcp會在底層累計數據長度到一定大小才一起發送,比如連續發送1字節的數據要累計到多個字節才發送,可以了解下tcp底層的Nagle算法。
3 ? 再就是我們提到的最簡單的情況,發送端緩沖區有上次未發送完的數據或者接收端的緩沖區里有未取出的數據導致數據粘連。
在這里插入圖片描述

4.2TCP粘包為什么會產生

1.TCP會發生粘包問題:TCP 是面向連接的傳輸協議,TCP 傳輸的數據是以流的形式,而流數據是沒有明確的開始結尾邊界,所以 TCP 也沒辦法判斷哪一段流屬于一個消息;TCP協議是流式協議;所謂流式協議,即協議的內容是像流水一樣的字節流,內容與內容之間沒有明確的分界標志,需要認為手動地去給這些協議劃分邊界。
粘包時:發送方每次寫入數據 < 接收方套接字(Socket)緩沖區大小。
拆包時:發送方每次寫入數據 > 接收方套接字(Socket)緩沖區大小。

2.UDP不會發生粘包問題:UDP具有保護消息邊界,在每個UDP包中就有了消息頭(UDP長度、源端口、目的端口、校驗和)。
粘包拆包問題在數據鏈路層、網絡層以及傳輸層都有可能發生。日常的網絡應用開發大都在傳輸層進行,由于UDP有消息保護邊界,不會發生粘包拆包問題,因此粘包拆包問題只發生在TCP協議中

4.3TCP粘包的解決方案

  1. 客戶端在發送數據包的時候,每個包都固定長度,比如1024個字節大小,如果客戶端發送的數據長度不足1024個字節,則通過補充空格的方式補全到指定長度;
  2. 客戶端在每個包的末尾使用固定的分隔符,例如\r\n,如果一個包被拆分了,則等待下一個包發送過來之后找到其中的\r\n,然后對其拆分后的頭部部分與前一個包的剩余部分進行合并,這樣就得到了一個完整的包;
  3. 將消息分為頭部和消息體,在頭部中保存有當前整個消息的長度,只有在讀取到足夠長度的消息之后才算是讀到了一個完整的消息;
  4. 通過自定義協議進行粘包和拆包的處理。

優缺點分析

  • 解決方案1:固定數據大小
    雖然這種方式可以解決粘包問題,但這種固定數據大小的傳輸方式,當數據量比較小時會使用空字符來填充,所以會額外的增加網絡傳輸的負擔,因此不是理想的解決方案。
  • 解決方案2:特殊字符結尾
    以特殊符號作為粘包的解決方案的最大優點是實現簡單,但存在一定的局限性,比如當一條消息中間如果出現了結束符就會造成半包的問題,所以如果是復雜的字符串要對內容進行編碼和解碼處理,這樣才能保證結束符的正確性。
  • 解決方案4:設置消息頭
    此解決方案可以解決粘包問題,并且對于空間的利用也相對高
  • 解決方案4:自定義請求協議
    此解決方案雖然可以解決粘包問題,但消息的設計和代碼的實現復雜度比較高,所以也不是理想的解決方案

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/73312.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/73312.shtml
英文地址,請注明出處:http://en.pswp.cn/web/73312.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

華為OD機試2025A卷七日集訓第1期 - 按算法分類,由易到難,循序漸進,玩轉OD(Python/JS/C/C++)

目錄 一、適合人群二、本期訓練時間三、如何參加四、7日集訓第1期五、精心挑選21道高頻100分經典題目&#xff0c;作為入門。第1天、邏輯分析第2天、邏輯分析第3天、邏輯分析第4天、邏輯分析第5天、雙指針第6天、二叉樹第7天、回溯 六、集訓總結六、國內直接使用最新GPT-4.5、滿…

Qt 重入和線程安全

重入和線程安全 在整個文檔中&#xff0c;"重入"和 "線程安全 "這兩個術語被用來標記類和函數&#xff0c;以表明它們在多線程應用程序中的使用方式&#xff1a; 線程安全函數可以同時被多個線程調用&#xff0c;即使調用使用的是共享數據&#xff0c;因…

Elasticsearch:構建 AI 驅動的搜索體驗

Elasticsearch 介紹 當你開始使用 Elastic 時&#xff0c;你將使用 Elasticsearch Relevance Engine?&#xff08;ESRE&#xff09;&#xff0c;它專為 AI 搜索應用程序提供支持。借助 ESRE&#xff0c;你可以利用一整套開發者工具&#xff0c;包括 Elastic 的文本搜索、向量…

鴻蒙生態開發

鴻蒙生態開發概述 鴻蒙生態是華為基于開源鴻蒙&#xff08;OpenHarmony&#xff09;構建的分布式操作系統生態&#xff0c;旨在通過開放共享的模式連接智能終端設備、操作系統和應用服務&#xff0c;覆蓋消費電子、工業物聯網、智能家居等多個領域。以下從定義與架構、核心技術…

JVM如何處理Java中的精度轉換: 從源碼到字節碼

你好&#xff0c;我是 shengjk1&#xff0c;多年大廠經驗&#xff0c;努力構建 通俗易懂的、好玩的編程語言教程。 歡迎關注&#xff01;你會有如下收益&#xff1a; 了解大廠經驗擁有和大廠相匹配的技術等 希望看什么&#xff0c;評論或者私信告訴我&#xff01; 文章目錄 一…

vue-next-admin修改配置指南

官方文檔地址&#xff1a;vue-next-admin 1.如何開啟側邊欄logo 在scr-layout-navbars-topBar-setings.vue中添加 getThemeConfig.value.isShowLogo true; 設置為true即可默認打開 2.修改側邊欄頂部的logo與文字 先把想要的圖標存到我的項目然后下載 然后把后面的幾個文件拉…

gin學習

gin學習筆記&#xff0c;不僅包含了基本的增刪查改外&#xff0c;還包括參數傳遞&#xff0c;上傳下載&#xff0c;模版、session與中間件等&#xff0c;方便收藏自習可用 文章目錄 獲得個請求get打印字符串get請求xmlget請求跳轉http方法路由可以通過Context的Param方法來獲取…

Flutter運行錯誤:UG! exception in phase ‘semantic analysis‘

最近在Mac Mini M4上通過Android Studio導入Flutter項目并運行&#xff0c;結果一直跑不起來&#xff0c;錯誤日志如下&#xff1a; 執行命令查看版本信息&#xff1a; flutter doctor --verbose通過輸出信息Java version OpenJDK Runtime Environment (build 21.0.41242208…

【計算機網絡運輸層詳解】

文章目錄 一、前言二、運輸層的功能1. 端到端通信2. 復用與分用3. 差錯檢測4. 流量控制5. 擁塞控制 三、運輸層協議&#xff1a;TCP 和 UDP1. TCP&#xff1a;面向連接的可靠傳輸協議2. UDP&#xff1a;無連接的傳輸協議 四、端口號與進程通信1. 端口號分類2. 端口通信模型 五、…

51單片機和STM32 入門分析

51單片機和STM32是嵌入式開發中兩種主流的微控制器&#xff0c;它們在架構、性能、應用場景等方面存在顯著差異。以下是兩者的對比分析及選擇建議&#xff1a; 1. 51單片機與STM32的定義與特點 51單片機 定義&#xff1a;基于Intel 8051內核的8位微控制器&#xff0c;結構簡單…

開源視覺語言模型MiniMax-VL-01:動態分辨率+4M超長文本,性能比肩GPT-4o

在人工智能領域&#xff0c;構建能夠像人類一樣理解、思考和行動的智能體&#xff08;AI Agent&#xff09;一直是研究人員的終極目標之一。而實現這一目標的關鍵在于模型是否具備足夠強大的感知能力、記憶能力和推理能力。近期&#xff0c;國內人工智能公司MiniMax重磅開源了其…

excel 列單元格合并(合并列相同行)

代碼 首先自定義注解CellMerge&#xff0c;用于標記哪些屬性需要合并&#xff0c;哪個是主鍵**&#xff08;這里做了一個優化&#xff0c;可以標記多個主鍵&#xff09;** import org.dromara.common.excel.core.CellMergeStrategy;import java.lang.annotation.*;/*** excel…

flowable適配達夢7 (2.1)

經過第一版的問題解決&#xff0c;后端項目可以啟動&#xff0c;前端頁面也集成進去。 前端在流程設計頁面報錯 之后發現主要是組件中modelerStore這個值沒有 解決方法:在data增加對象 給component/process/designer.vue 中涉及到的每個子組件傳入 :modelerStore“modeler…

Prometheus Exporter系列-Mysql_Exporter一鍵部署

新項目舊項目都需要給研發配置mysql監控&#xff0c;這里mysql監控對應aws 阿里云 騰訊云 華為云的云mysql產品或開源自建mysql。 exporter安裝雖然簡單&#xff0c;經常手動操作不免讓人心煩&#xff0c;一鍵完成省去繁瑣的常規操作。 配置信息對的情況下測試多次都可以正常安…

2025年移動端開發性能優化實踐與趨勢分析

啟動速度優化 本質&#xff1a;縮短首次可見幀渲染時間。 方法&#xff1a; iOS&#xff1a;利用Core ML本地模型輕量化部署&#xff0c;減少云端等待。Android&#xff1a;強制啟用SplashScreen API&#xff0c;通過setKeepOnScreenCondition控制動畫時長。冷啟動需將耗時操…

【MySQL篇】DEPENDENT SUBQUERY(依賴性子查詢)優化:從百秒到秒級響應的四種優化辦法

&#x1f4ab;《博主介紹》&#xff1a;?又是一天沒白過&#xff0c;我是奈斯&#xff0c;從事IT領域? &#x1f4ab;《擅長領域》&#xff1a;??擅長阿里云AnalyticDB for MySQL(分布式數據倉庫)、Oracle、MySQL、Linux、prometheus監控&#xff1b;并對SQLserver、NoSQL(…

全文 - MLIR Toy Tutorial Chapter 1: Toy Language and AST

Toy 語言 本教程&#xff0c;將會借助一個玩具語言來講解&#xff0c;這個語言我們稱其為 Toy。Toy 是一個基于張量的語言&#xff0c;它允許你定義函數&#xff0c;執行一些數學計算&#xff0c;并且打印結果。做這樣的設定&#xff0c;是因為我們希望讓教程保持簡明&#xff…

排序復習_代碼純享

頭文件 #pragma once #include<iostream> #include<vector> #include<utility> using std::vector; using std::cout; using std::cin; using std::endl; using std::swap;//插入排序 //1、直接插入排序&#xff08;穩定&#xff09; void InsertSort(vecto…

CSS語言的雙向鏈表

CSS語言的雙向鏈表 引言 在計算機科學中&#xff0c;數據結構是一個極為重要的概念&#xff0c;而鏈表則是最常見的數據結構之一。鏈表可以分為單向鏈表和雙向鏈表&#xff0c;其中雙向鏈表因其靈活性和高效性而受到廣泛應用。在前端開發的領域&#xff0c;尤其是CSS&#xf…

簡單理解機器學習中top_k、top_p、temperature三個參數的作用

AI系列文章&#xff1a; AWS AI認證考試中經常提及幾個重要的工具介紹 簡單理解機器學習中top_k、top_p、temperature三個參數的作用 用Deepseek Kimi 快速生成高質量的ppt 在機器學習中&#xff0c;top_k、top_p 和 temperature 是用于控制生成模型&#xff08;如語言模型…