Qt網絡編程

前言

Qt為了支持跨平臺,對系統網絡編程的API(socket API)也進行了重新分裝。

實際Qt中進行網絡編程也不一定使用Qt封裝的網絡API,也有可能使用的是系統原生API或者其他第三方框架的API。

若要使用Qt中的網絡編程的API,則要在項目中的.pro文件中添加network模塊。

我們學過的Qt的各種控件等常用的功能都是包含在QtCore模塊中的,系統默認添加了,為啥Qt要和劃分出這些模塊呢?Qt本身就是一個非常龐大的框架,如果把所有的Qt功能都放在一起時,即使我們的寫的代碼非常簡單,此時生成的可執行程序也很龐大,因為包含了大量沒有使用的功能

進行網絡編程的時候,本質上是在編寫應用層代碼,需要傳輸層(操作系統內核實現好的)提供支持,socket API就是傳輸層提供給應用層的接口,傳輸層最核心的協議有UDP(無連接,不可靠傳輸,面向數據報,全雙工)和TCP(有連接,可靠傳輸,面向字節流,全雙工),并且這兩協議差別還挺大,就導致在編寫代碼的時候也是有所差別的,因此操作系統就提供了兩套socket API,所以Qt也提供了兩套API,來完成UDP和TCP的開發。

一、UDP Socket

1、核心API

主要的類有兩個:QUdpSocket 和 QNetworkDatagram,其中QUdpSocket表示一個UDP的文件,QNetworkDatagram:由于UDP是面向數據報的,每次傳輸的時候,發送和接受都是一個完整的NetworkDatagram。

QUdpSocket:

當socket收到請求后,QUdpSocket就會觸發這個readyRead信號,就可以在信號槽里完成讀取請求的操作了。

QNetworkDategram:

2、事例?

寫一個帶有界面的UDP回顯服務器

服務器端

//Widget.h#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include<QUdpSocket>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private:Ui::Widget *ui;//先創建UdpSocket成員QUdpSocket* socket;//服務器的核心邏輯void processRequest();//服務器業務QString process(const QString& request);
};
#endif // WIDGET_H
//Widget.cpp#include "widget.h"
#include "ui_widget.h"#include<QMessageBox>
#include<QNetworkDatagram>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//創建出這個對象socket = new QUdpSocket(this);this->setWindowTitle("服務器");//連接信號槽connect(socket,&QUdpSocket::readyRead,this,&Widget::processRequest);//綁定端口號,必須在綁定端口號之前鏈接信號槽//如果在綁定完成之后,連接信號槽之前的話,若有客戶端把請求發過來的,//此時就可能讀不到這樣的請求//QHostAddress::Any表示不管有多少網卡都可綁定上去,//端口號可自己寫但要在范圍內(0~65536)bool ret = socket->bind(QHostAddress::Any,9090);//一個端口號只能被一個socket綁定,若9090被別人綁定的話就會失敗if(!ret){//綁定失敗QMessageBox::critical(this,"服務器啟動出錯",socket->errorString());return;}}Widget::~Widget()
{delete ui;
}//服務器的核心邏輯
void Widget::processRequest()
{//1、讀取數據const QNetworkDatagram& requestDatagram = socket->receiveDatagram();//2、解析QString request = requestDatagram.data();//雖然它返回的是QBetyArry,其是可以賦值給QString//3、根據請求計算響應(回顯服務器,響應不需要計算,就是請求本身)const QString& response = process(request);//4、把響應發送給客戶端QNetworkDatagram responseDategram(response.toUtf8(), requestDatagram.senderAddress(), requestDatagram.senderPort());socket->writeDatagram(responseDategram);// 把這次交互的信息, 顯示到界面上.QString log = "[" + requestDatagram.senderAddress().toString() + ":" + QString::number(requestDatagram.senderPort())+ "] req: " + request + ", resp: " + response;ui->listWidget->addItem(log);
}QString Widget::process(const QString &request)
{return request;
}

客戶端

//Widget.h#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include<QUdpSocket>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();private:Ui::Widget *ui;QUdpSocket* socket;void processResponse();
};
#endif // WIDGET_H
//Widget.cpp#include "widget.h"
#include "ui_widget.h"#include<QNetworkDatagram>const QString& SERVER_IP  = "127.0.0.1";
const quint16 SERVER_PORT = 9090;Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);socket = new QUdpSocket(this);this->setWindowTitle("客戶端");//連接槽函數connect(socket,&QUdpSocket::readyRead,this,&Widget::processResponse);}Widget::~Widget()
{delete ui;
}void Widget::processResponse()
{// 通過這個函數來處理收到的響應.// 1. 讀取到響應數據const QNetworkDatagram& responseDatagram = socket->receiveDatagram();QString response = responseDatagram.data();// 2. 把響應數據顯示到界面上.ui->listWidget->addItem("服務器說: " + response);
}void Widget::on_pushButton_clicked()
{//1、獲取到輸入框的內容const QString& text = ui->lineEdit->text();//2、構造UDP的請求數據QNetworkDatagram requestDatagram(text.toUtf8(),QHostAddress(SERVER_IP), SERVER_PORT);//3發送請求數據socket->writeDatagram(requestDatagram);// 4. 把發送的請求也添加到列表框中.ui->listWidget->addItem("客戶端說: " + text);// 5. 把輸入框的內容也清空一下.ui->lineEdit->setText("");}

二、TCP Socket

1、核心API

核心類有:QTcpServer 和 QTcpSocket。

QTcpServer用于監聽端口和獲取客戶端連接。

QTcpSocket用于客戶端和服務端之間的數據交互?。

?peerAddress和peerPort用于獲取對端的地址和端口,

connectToHost("地址",端口號);和服務器建立連接,

waitForConnected(),等待連接建立的結果,確認是否連接成功,返回bool類型

?QByteArry用于表示一個字節數組,可以很方便的和QString進行相互轉換,使用QString的構造函數可以把QByteArray轉成QString,使用QString的toUtf8 函數可以把QString轉成QByteArray

2、事例(寫一個TCP回顯服務器)

在學習liunx時寫的TCP的回顯服務器的時候,遇到了一個問題就是多個客戶端訪問的時候就只有一個生效,引入多線程給每個客戶端安排一個單獨的線程問題才解決?

服務端

//Widget.h#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include<QTcpServer>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void processConnection();QString process(const QString );private:Ui::Widget *ui;//QTcpServer* tcpServer;};
#endif // WIDGET_H

//Widget.cpp#include "widget.h"
#include "ui_widget.h"#include<QTcpSocket>
#include<QMessageBox>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//修改窗口標題this->setWindowTitle("服務器");//實例化QUdpsocket對象tcpServer = new QTcpServer(this);//綁定槽函數,當有新的客戶端建立好連接后connect(tcpServer,&QTcpServer::newConnection,this,&Widget::processConnection);//綁定+監聽//這和操作得是初始化的最后一步,都是需要把如何處理連接,如何處理請求等都準備好后才能真正綁定端口并監聽bool ret = tcpServer->listen(QHostAddress::Any,9090);if(!ret){QMessageBox::critical(this,"服務器啟動失敗",tcpServer->errorString());return;}
}Widget::~Widget()
{delete ui;
}void Widget::processConnection()
{//通過tcpServer拿到一個socket對象,通過這個對象來和客戶端進行通信QTcpSocket* clientSocket = tcpServer->nextPendingConnection();//顯示用戶上線                      //對端端口號QString log = "[" + clientSocket->peerAddress().toString() + ":" + QString::number(clientSocket->peerPort()) + "]" + "客戶端上線了";ui->listWidget->addItem(log);//如果有數據到達并準備就緒時,通過信號槽來處理客戶端發送請求的情況connect(clientSocket,&QTcpSocket::readyRead,this,[=](){//讀取請求數據QString req = clientSocket->readAll();//根據請求進行響應QString resp = process(req);//把響應寫回客戶端clientSocket->write(resp.toUtf8());//上述操作記錄到日志中QString log = "[" + clientSocket->peerAddress().toString() + QString::number(clientSocket->peerPort()) + "]"+ "res:" + req + "resp:" + resp;ui->listWidget->addItem(log);});//當客戶端斷開時connect(clientSocket,&QTcpSocket::disconnected,this,[=](){//顯示日志QString log = "[" + clientSocket->peerAddress().toString() + ":" +QString::number(clientSocket->peerPort()) + "]" + "客戶端下線";ui->listWidget->addItem(log);//手動釋放clientSocket//在下一輪事件循環在進行銷毀操作clientSocket->deleteLater();});
}QString Widget::process(const QString req)
{return req;
}

客戶端

//Widget.h#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QTcpSocket>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();private:Ui::Widget *ui;QTcpSocket* tcpSocket;
};
#endif // WIDGET_H
//Widget.cpp#include "widget.h"
#include "ui_widget.h"#include<QMessageBox>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);//設置窗口標題this->setWindowTitle("客戶端");//創建socket實例對象tcpSocket = new QTcpSocket(this);//和服務器建立連接tcpSocket->connectToHost("127.0.0.1",9090);//連接信號槽處理響應connect(tcpSocket,&QTcpSocket::readyRead,this,[=](){//讀取響應內容QString resp = tcpSocket->readAll();//把響應內容顯示到界面上ui->listWidget->addItem("服務器說:" + resp);});//等待連接建立的結果,確認是否連接成功bool ret = tcpSocket->waitForConnected();if(!ret){QMessageBox::critical(this,"連接服務器出錯",tcpSocket->errorString());exit(1);}
}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{QString text = ui->lineEdit->text();ui->lineEdit->clear();//把請求發送到服務端tcpSocket->write(text.toUtf8());//把發送的消息顯示到界面上ui->listWidget->addItem("客戶端說:" + text);
}

三、HTTP client

HTTP的使用相比于TCP/UDP更多一些,TCP/UDP是傳輸層的協議,所以還要在應用層搭配程序員自定義的協議完成工作,而HTTP是一個現成的應用協議,本身就提供了一些方便的拓展能力以及特殊功能,所以更多的時候用HTTP完成數據的交互。

HTTP協議本質上就是基于TCP協議實現的,所以實現一個HTTP客戶端/服務器也就是基于TCP Socket進行了封裝,Qt中只是提供了HTTP客戶端,而沒有提供HTTP服務器的庫。

1、核心API

關鍵類主要有三個:QNetworkAccessManager、QNetworkRequest、QNetworkReply

QNetworkAccessManger提供了HTTP的核心操作

?QNetworkRequest表示一個HTTP請求(不含body),如果需要發送帶有body的請求(比如post)會在QNetworkAccessManger的post方法中通過單獨的參數來傳入body

QVariant表示類型可變的值,類似于C語言中的void*

其中的QNetworkRequest::KnownHeaders是一個枚舉類型,常用取值:

QNetworkReply表示一個HTTP 響應.這個類同時也是QIODevice的子類。

?此外,QNetworkReply還有一個重要的信號finished會在客戶端收到完整的響應數據之后觸發。

?2、事例(給服務器發送一個get請求)

//Widget.h#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QNetworkAccessManager>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();private slots:void on_pushButton_clicked();private:Ui::Widget *ui;QNetworkAccessManager* manager;
};
#endif // WIDGET_H
//Widget.cpp#include "widget.h"
#include "ui_widget.h"#include<QUrl>
#include<QNetworkRequest>
#include<QNetworkReply>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);this->setWindowTitle("客戶端");manager = new QNetworkAccessManager(this);}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{// 1. 獲取到輸入框中的 url.QUrl url(ui->lineEdit->text());// 2. 構造一個 HTTP 請求對象QNetworkRequest request(url);// 3. 發送請求QNetworkReply* response = manager->get(request);// 4. 通過信號槽, 來處理響應connect(response, &QNetworkReply::finished, this, [=]() {if (response->error() == QNetworkReply::NoError) {// 響應正確獲取到了.QString html = response->readAll();ui->plainTextEdit->setPlainText(html);} else {// 響應出錯了ui->plainTextEdit->setPlainText(response->errorString());}// 還需要對 response 進行釋放.response->deleteLater();});
}

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

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

相關文章

矩陣短劇系統:如何用1個后臺管理100+小程序?技術解析與實戰應用

引言&#xff1a;短劇行業的效率革命 2025年&#xff0c;短劇市場規模已突破千億&#xff0c;但傳統多平臺運營模式面臨重復開發成本高、用戶數據分散、內容同步效率低等痛點。行業亟需一種既能降本增效又能聚合流量的解決方案——“矩陣短劇系統”。通過“1個后臺管理100小程…

嵌入式STM32學習——ESP8266 01S的基礎介紹

簡介 ESP8266 系列模組是深圳市安信可科技有限公司開發的一系列基于樂鑫ESP8266EX的低功耗UART-WiFi芯片模組&#xff0c;可以方便地進行二次開發&#xff0c;接入云端服務&#xff0c;實現手機3/4G全球隨時隨地的控制&#xff0c;加速產品原型設計。 模塊核心處理器 ESP8266 在…

form-create-designer中$inject參數的數據結構及各項屬性說明

FcDesigner 是一款基于Vue的開源低代碼可視化表單設計器工具&#xff0c;通過數據驅動表單渲染。可以通過拖拽的方式快速創建表單&#xff0c;提高開發者對表單的開發效率&#xff0c;節省開發者的時間。并廣泛應用于在政務系統、OA系統、ERP系統、電商系統、流程管理等領域。 …

Jasypt概述及整合SpringBoot實現敏感數據加密

前言 在實際開發中&#xff0c;Spring Boot應用的配置文件中經常包含數據庫密碼、API密鑰等敏感信息。如果這些信息以明文形式存儲&#xff0c;會帶來嚴重的安全隱患。本文將詳細介紹如何使用Jasypt&#xff08;Java Simplified Encryption&#xff09;對Spring Boot配置文件中…

Better Faster Large Language Models via Multi-token Prediction 原理

目錄 模型結構&#xff1a; Memory-efficient implementation&#xff1a; 實驗&#xff1a; 1. 在大規模模型上效果顯著&#xff1a; 2. 在不同類型任務上的效果&#xff1a; 為什么MLP對效果有提升的幾點猜測&#xff1a; 1. 并非所有token對生成質量的影響相同 2. 關…

git merge解沖突后,add、continue提交

git merge解沖突后&#xff0c;add、continue提交 git merge操作沖突后&#xff0c;需要手動解沖突&#xff0c;解完沖突后&#xff0c;需要: git add . 然后&#xff0c;進入一般的正常git代碼提交流程。 git合并‘merge’其他分支的個別文件到當前branch_gitbash 合并branc…

3.8.1 利用RDD實現詞頻統計

在本次實戰中&#xff0c;我們通過Spark的RDD實現了詞頻統計功能。首先&#xff0c;準備了包含單詞的文件并上傳至HDFS。接著&#xff0c;采用交互式方式逐步完成詞頻統計&#xff0c;包括創建RDD、單詞拆分、映射為二元組、按鍵歸約以及排序等操作。此外&#xff0c;還通過創建…

應對進行性核上性麻痹,健康護理鑄就溫暖防線

進行性核上性麻痹&#xff08;PSP&#xff09;是一種罕見的神經退行性疾病&#xff0c;主要影響患者的運動、平衡及吞咽等功能。針對這類患者&#xff0c;有效的健康護理對提升其生活質量、延緩病情發展至關重要。 在日常生活護理方面&#xff0c;由于患者存在平衡障礙和肌肉僵…

融合蛋白質語言模型和圖像修復模型,麻省理工與哈佛聯手提出PUPS ,實現單細胞級蛋白質定位

蛋白質亞細胞定位&#xff08;subcellular localization of a protein&#xff09;是指蛋白質在細胞結構中具體的定位情況&#xff0c; 這對蛋白質行使其生物學功能至關重要。舉個簡單例子&#xff0c;如果把細胞想象成一個龐大的企業&#xff0c;其中細胞核、線粒體、細胞膜等…

lanqiaoOJ 4330:歐拉函數模板

【題目來源】 https://www.lanqiao.cn/problems/4330/learning/ 【問題描述】 這是一道模板題。 首先給出歐拉函數的定義&#xff1a;即 φ(n) 表示的是小于等于 n 的數中和 n 互質的數的個數。 比如說 φ(6)2&#xff0c;當 n 是質數的時候&#xff0c;顯然有φ(n)n-1。 【題…

無人機電子防抖技術要點概述!

一、技術要點 1. 傳感器數據融合 電子防抖需結合陀螺儀、加速度計、視覺傳感器等多源數據&#xff0c;實時檢測無人機的姿態變化和振動頻率。例如&#xff0c;IMU&#xff08;慣性測量單元&#xff09;通過加速度計和陀螺儀測量飛行器的姿態和運動狀態&#xff0c;結合視覺感…

Win10 安裝單機版ES(elasticsearch),整合IK分詞器和安裝Kibana

一. 先查看本機windows是否安裝了ES(elasticsearch)&#xff0c;檢查方法如下&#xff1a; 檢查進程 按 Ctrl Shift Esc 組合鍵打開 “任務管理器”。在 “進程” 選項卡中&#xff0c;查看是否有 elasticsearch 相關進程。如果有&#xff0c;說明系統安裝了 ES。 檢查端口…

BIO、NIO、AIO 的區別與實戰應用解析

導語&#xff1a; BIO、NIO 和 AIO 是后端面試中的經典話題&#xff0c;尤其在高并發、高性能場景下更是重中之重。本文將從面試官視角出發&#xff0c;深入剖析三者的區別、典型題目和實戰解答&#xff0c;助你掌握答題技巧&#xff0c;輕松拿下這一高頻考點&#xff01; 一、…

電腦風扇轉速不正常的原因

一、硬件故障或接觸問題 1. 風扇本身損壞 扇葉卡頓或軸承磨損&#xff1a;灰塵堆積、異物纏繞&#xff08;如頭發、線纜&#xff09;會導致扇葉轉動阻力增大&#xff0c;發出異響并轉速下降&#xff1b;軸承潤滑脂干涸或老化會引起風扇噪音大、轉速不穩定。電機故障&#xff…

運維打鐵:生產服務器用戶權限管理方案全解析

文章目錄 一、引言二、方案設計2.1 權限模型選擇2.2 角色定義2.3 權限分配2.4 用戶與角色關聯 三、相關代碼注釋&#xff08;以 Linux 系統為例&#xff09;3.1 用戶創建與角色分配腳本3.2 權限設置腳本 四、常見問題解決4.1 用戶無法登錄4.2 用戶權限不足4.3 權限文件修改后不…

在tp6模版中加減法

實際項目中&#xff0c;我們經常需要標簽變量加減運算的操作。但是&#xff0c;在ThinkPHP中&#xff0c;并不支持模板變量直接運算的操作。幸運的是&#xff0c;它提供了自定義函數的方法&#xff0c;我們可以利用自定義函數解決&#xff1a;ThinkPHP模板自定義函數語法如下&a…

Fastjson利用鏈JdbcRowSetImpl分析

首先創建客戶端 package com.yq1ng.vul;import com.alibaba.fastjson.JSON;/*** FastJsonTest** author yq1ng* date 2021/12/29 19:45* since 1.0.0*/ public class FastJsonTest {public static void main(String[] args) {String ser "{\"type\":\"co…

基于OAuth2-proxy和Keycloak為comfyui實現SSO

背景 comfyui無認證被漏掃后易被rce挖礦 攻擊過程 https://www.oschina.net/news/340226 https://github.com/comfyanonymous/ComfyUI/discussions/5165 阿里云漏洞庫關于comfyui的漏洞 https://avd.aliyun.com/search?qcomfyui&timestamp__1384n4%2BxBD0GitGQ0QD8ID%2F…

第R7周:糖尿病預測模型優化探索

文章目錄 1.數據預處理1.1 設置GPU1.2 數據導入1.3 數據檢查 2. 數據分析2.1 數據分布分析2.2 相關性分析 3. LSTM模型3.1 劃分數據集3.2 數據集構建3.3 定義模型 4. 訓練模型4.1 定義訓練函數4.2 定義測試函數4.3 訓練模型 5. 模型評估5.1 Loss與Accuracy圖 6. 總結 &#x1f…

一些好用的Chrome 擴展程序

以下是按主要功能分類的 Chrome 擴展程序列表&#xff0c;包括其版本號、中文功能簡述以及指向其主頁或 Chrome 網上應用店頁面的鏈接。 翻譯與語言 沉浸式翻譯 - 網頁翻譯插件 | PDF 翻譯 | 免費 版本: 1.16.12 描述: 【沉浸式翻譯】免費的&#xff08;原文 / 譯文&#xff0…