基于QT編寫一個網(wǎng)絡(luò)調(diào)試助手
項(xiàng)目介紹
軟件界面

gitee界面:

1 TCP網(wǎng)絡(luò)調(diào)試助手
1.1項(xiàng)目概述
- 網(wǎng)絡(luò)相關(guān)的一些基礎(chǔ)概念
- 學(xué)習(xí)QTcpServer
- 學(xué)習(xí)QTcpClient
- 學(xué)習(xí)TextEdit特定位置輸入文字顏色
- 學(xué)習(xí)網(wǎng)絡(luò)通信相關(guān)知識點(diǎn)
- 復(fù)習(xí)鞏固之前UI控件
- 程序運(yùn)行如下圖所示

1.2開發(fā)流程

1.3 QTtcp服務(wù)器的關(guān)鍵流程
工程建立,需要在.pro加入網(wǎng)絡(luò)權(quán)限
QT network+= core gui
創(chuàng)建一個基于"QTcpserver"的服務(wù)端涉及以下關(guān)鍵步驟:
1.創(chuàng)建并初始化 QTcpserver 實(shí)例
實(shí)例化 QTcpserver。
o:調(diào)用"listen,方法在特定端口監(jiān)聽傳入的連接。
2.處理新連接:
為 newconnection"信號連接一個槽函數(shù)。在槽函數(shù)中,使用"nextPendingconnection 獲取'Qfcpsocket,以與客戶端通信。
3.讀取和發(fā)送數(shù)據(jù):
通過連接"qTcpsocket 的:readyRead 信號來讀取來自客戶端的數(shù)據(jù)。'使用write"方法發(fā)送數(shù)據(jù)回客戶端。
4.關(guān)閉連接:
適當(dāng)?shù)臅r候關(guān)閉 QTcpSocket 。
class MyServer : public QObject {
Q_OBJECT
public:
MyServer() {
QTcpServer *server = new QTcpServer(this);
connect(server, &QTcpServer::newConnection, this,
&MyServer::onNewConnection);
server->listen(QHostAddress::Any, 1234);
}
private slots:
void onNewConnection() {
QTcpSocket *clientSocket = server->nextPendingConnection();
connect(clientSocket, &QTcpSocket::readyRead, this,
&MyServer::onReadyRead);
// ...
}
void onReadyRead() {
QTcpSocket *clientSocket = qobject_cast<QTcpSocket *>(sender());
// 讀取數(shù)據(jù)
QByteArray data = clientSocket->readAll();
// 處理數(shù)據(jù)
// ...
}
};確保在使用 QTcpServer 和 QTcpSocket 時妥善處理網(wǎng)絡(luò)錯誤和異常情況。
1.4 QTtcp客戶端的關(guān)鍵流程
創(chuàng)建一個基于 QTcpSocket 的Qt客戶端涉及以下步驟:
1. 創(chuàng)建 QTcpSocket 實(shí)例:
實(shí)例化 QTcpSocket 。
2. 連接到服務(wù)器:
使用 connectToHost 方法連接到服務(wù)器的IP地址和端口。
3. 發(fā)送數(shù)據(jù)到服務(wù)器:
使用 write 方法發(fā)送數(shù)據(jù)。
4. 接收來自服務(wù)器的數(shù)據(jù):
為 readyRead 信號連接一個槽函數(shù)來接收數(shù)據(jù)。
5. 關(guān)閉連接:
關(guān)閉 QTcpSocket 連接。
示例代碼如下:
class MyClient : public QObject {
Q_OBJECT
public:
MyClient() {
QTcpSocket *socket = new QTcpSocket(this);
connect(socket, &QTcpSocket::readyRead, this, &MyClient::onReadyRead);
socket->connectToHost("server_address", 1234);
}
private slots:
void onReadyRead() {
QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
QByteArray data = socket->readAll();
// 處理接收到的數(shù)據(jù)
// ...
}
};2 TCP協(xié)議
以下內(nèi)容自省閱讀和消化,主要在面試之前類似八股文問答,實(shí)際編程我們不需要關(guān)系這么多,QTcpSocket類底下的API已經(jīng)做好所有的封裝。
TCP(傳輸控制協(xié)議)是一種廣泛使用的網(wǎng)絡(luò)通信協(xié)議,設(shè)計(jì)用于在網(wǎng)絡(luò)中的計(jì)算機(jī)之間可靠地傳輸數(shù)據(jù)。它是互聯(lián)網(wǎng)協(xié)議套件的核心部分,通常與IP(互聯(lián)網(wǎng)協(xié)議)一起使用,合稱為TCP/IP。以下是TCP協(xié)議的一些基本特點(diǎn):
1. 面向連接:在數(shù)據(jù)傳輸之前,TCP 需要在發(fā)送方和接收方之間建立一個連接。這包括三次握手過程,確保兩端都準(zhǔn)備好進(jìn)行數(shù)據(jù)傳輸。
2. 可靠傳輸:TCP 提供可靠的數(shù)據(jù)傳輸服務(wù),這意味著它保證數(shù)據(jù)包準(zhǔn)確無誤地到達(dá)目的地。如果發(fā)生數(shù)據(jù)丟失或錯誤,TCP 會重新發(fā)送數(shù)據(jù)包。
3. 順序控制:TCP 保證數(shù)據(jù)包的傳輸順序。即使數(shù)據(jù)包在網(wǎng)絡(luò)中的傳輸順序被打亂,接收方也能按照正確的順序重組這些數(shù)據(jù)。
4. 流量控制:TCP 使用窗口機(jī)制來控制發(fā)送方的數(shù)據(jù)傳輸速率,以防止網(wǎng)絡(luò)過載。這有助于防止接收方被發(fā)送方發(fā)送的數(shù)據(jù)所淹沒。
5. 擁塞控制:TCP 還包括擁塞控制機(jī)制,用來檢測并防止網(wǎng)絡(luò)擁塞。當(dāng)網(wǎng)絡(luò)擁塞發(fā)生時,TCP 會減少其數(shù)據(jù)傳輸速率。
6. 數(shù)據(jù)分段:大塊的數(shù)據(jù)在發(fā)送前會被分割成更小的段,以便于傳輸。這些段會被獨(dú)立發(fā)送并在接收端重新組裝。
7. 確認(rèn)和重傳:接收方對成功接收的數(shù)據(jù)包發(fā)送確認(rèn)(ACK)信號。如果發(fā)送方?jīng)]有收到確認(rèn),它會重傳丟失的數(shù)據(jù)包。
8. 終止連接:數(shù)據(jù)傳輸完成后,TCP 連接需要被正常關(guān)閉,這通常涉及到四次揮手過程。
TCP 適用于需要高可靠性的應(yīng)用,如網(wǎng)頁瀏覽、文件傳輸、電子郵件等。然而,由于它的這些特性,TCP在處理速度上可能不如其他協(xié)議(如UDP)那么快速。
TCP協(xié)議中的三次握手和四次揮手是建立和終止連接的重要過程。下面是它們的簡要描述:
三次握手(建立連接)
三次握手的主要目的是在兩臺設(shè)備之間建立一個可靠的連接。它包括以下步驟:
1. SYN:客戶端向服務(wù)器發(fā)送一個SYN(同步序列編號)報(bào)文來開始一個新的連接。此時,客戶端進(jìn)
入SYN-SENT狀態(tài)。
2. SYN-ACK:服務(wù)器接收到SYN報(bào)文后,回復(fù)一個SYN-ACK(同步和確認(rèn))報(bào)文。此時服務(wù)器進(jìn)入
SYN-RECEIVED狀態(tài)。
3. ACK:客戶端接收到SYN-ACK后,發(fā)送一個ACK(確認(rèn))報(bào)文作為回應(yīng),并進(jìn)入ESTABLISHED(已
建立)狀態(tài)。服務(wù)器在收到這個ACK報(bào)文后,也進(jìn)入ESTABLISHED狀態(tài)。這標(biāo)志著連接已經(jīng)建立。

四次揮手(斷開連接)
四次揮手的目的是終止已經(jīng)建立的連接。這個過程包括以下步驟:
1. FIN:當(dāng)通信的一方完成數(shù)據(jù)發(fā)送任務(wù)后,它會發(fā)送一個FIN(結(jié)束)報(bào)文來關(guān)閉連接。發(fā)送完FIN報(bào)文后,該方進(jìn)入FIN-WAIT-1狀態(tài)。
2. ACK:另一方接收到FIN報(bào)文后,發(fā)送一個ACK報(bào)文作為回應(yīng),并進(jìn)入CLOSE-WAIT狀態(tài)。發(fā)送FIN報(bào)文的一方在收到ACK后,進(jìn)入FIN-WAIT-2狀態(tài)。
3. FIN:在等待一段時間并完成所有數(shù)據(jù)的發(fā)送后,CLOSE-WAIT狀態(tài)的一方也發(fā)送一個FIN報(bào)文來請求關(guān)閉連接。
4. ACK:最初發(fā)送FIN報(bào)文的一方在收到這個FIN報(bào)文后,發(fā)送一個ACK報(bào)文作為最后的確認(rèn),并進(jìn)入TIME-WAIT狀態(tài)。經(jīng)過一段時間后,確保對方接收到了最后的ACK報(bào)文,該方最終關(guān)閉連接。

在這兩個過程中,三次握手主要確保雙方都準(zhǔn)備好進(jìn)行通信,而四次揮手則確保雙方都已經(jīng)完成通信并同意關(guān)閉連接。
3 Socket
Socket 不是一個協(xié)議,而是一種編程接口(API)或機(jī)制,用于在網(wǎng)絡(luò)中實(shí)現(xiàn)通信。Socket 通常在應(yīng)用
層和傳輸層之間提供一個端點(diǎn),使得應(yīng)用程序可以通過網(wǎng)絡(luò)發(fā)送和接收數(shù)據(jù)。它支持多種協(xié)議,主要是
TCP 和 UDP。 以下是 Socket 的一些基本特點(diǎn):
類型:有兩種主要類型的 Sockets —— TCP Socket(面向連接,可靠)和 UDP Socket(無連接,不可靠)。
應(yīng)用:在各種網(wǎng)絡(luò)應(yīng)用中廣泛使用,如網(wǎng)頁服務(wù)器、聊天應(yīng)用、在線游戲等。
編程語言支持:大多數(shù)現(xiàn)代編程語言如 Python, Java, C++, 等都提供 Socket 編程的支持。
功能:提供了創(chuàng)建網(wǎng)絡(luò)連接、監(jiān)聽傳入的連接、發(fā)送和接收數(shù)據(jù)等功能。
QT: 在QT組件中,QTcpSocket用來管理和實(shí)現(xiàn)TCP Socket通信,QUdpSocket用來管理和實(shí)現(xiàn)
UDP Socket通信
總之,Socket 是實(shí)現(xiàn)網(wǎng)絡(luò)通信的基礎(chǔ)工具之一,它抽象化了網(wǎng)絡(luò)層的復(fù)雜性,為開發(fā)者提供了一種相對 簡單的方式來建立和管理網(wǎng)絡(luò)連接。
4 創(chuàng)建TCP服務(wù)端的核心代碼
主要步驟如下:
1. 創(chuàng)建 QTcpServer 實(shí)例:啟動服務(wù)器并開始監(jiān)聽指定端口。
2. 監(jiān)聽連接請求:調(diào)用 listen() 方法使服務(wù)器監(jiān)聽特定的 IP 地址和端口。
3. 接受連接:當(dāng)客戶端嘗試連接時, QTcpServer 產(chǎn)生一個信號。你需要實(shí)現(xiàn)一個槽(slot)來響應(yīng)這個信號,并接受連接。
4. 處理客戶端連接:每個連接的客戶端都關(guān)聯(lián)一個 QTcpSocket 對象,用于數(shù)據(jù)交換。
示例代碼
#include <QTcpServer>
#include <QTcpSocket>
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QTcpServer server;
// 監(jiān)聽端口
if (!server.listen(QHostAddress::Any, 12345)) {
qDebug() << "Server could not start";
return -1;
}
qDebug() << "Server started!";
// 當(dāng)有新連接時,執(zhí)行相應(yīng)的操作
QObject::connect(&server, &QTcpServer::newConnection, [&]() {
QTcpSocket *client = server.nextPendingConnection();
QObject::connect(client, &QTcpSocket::readyRead, [client]() {
QByteArray data = client->readAll();
qDebug() << "Received data:" << data;
client->write("Hello, client!");
});
QObject::connect(client, &QTcpSocket::disconnected, client,
&QTcpSocket::deleteLater);
});
return a.exec();
}代碼解釋
1. 創(chuàng)建 QTcpServer 對象:在主函數(shù)中,直接創(chuàng)建了一個 QTcpServer 對象。
2. 監(jiān)聽端口:使用 listen() 方法監(jiān)聽所有接口上的 12345 端口。
3. 處理新連接:通過連接 newConnection 信號,當(dāng)有新客戶端連接時,會調(diào)用相應(yīng)的槽函數(shù)。
4. 讀取數(shù)據(jù):為每個連接的客戶端創(chuàng)建 QTcpSocket 對象,并連接 readyRead 信號以接收數(shù)據(jù)。
5. 發(fā)送數(shù)據(jù):向客戶端發(fā)送響應(yīng)消息。
6. 客戶端斷開連接時的處理:使用 disconnected 信號確??蛻舳嗽跀嚅_連接時被適當(dāng)?shù)厍謇怼?/p>
這個代碼示例展示了如何使用 QTcpServer 創(chuàng)建一個基本的 TCP 服務(wù)器,而無需通過繼承來擴(kuò)展類。這 種方式通常更簡單,適用于不需要復(fù)雜處理的基本應(yīng)用場景。
5 創(chuàng)建TCP客戶端的核心代碼
為了使客戶端代碼更具模塊化和響應(yīng)性,可以使用 Qt 的信號與槽機(jī)制。這種方法允許客戶端以事件驅(qū)動 的方式響應(yīng)網(wǎng)絡(luò)事件,如連接建立、數(shù)據(jù)接收等。下面是一個使用信號與槽的 TCP 客戶端示例。
示例代碼
#include <QTcpSocket>
#include <QCoreApplication>
#include <QDebug>
class TcpClient : public QObject {
Q_OBJECT
public:
TcpClient(const QString &host, quint16 port) {
connect(&socket, &QTcpSocket::connected, this, &TcpClient::onConnected);
connect(&socket, &QTcpSocket::readyRead, this, &TcpClient::onReadyRead);
socket.connectToHost(host, port);
}
private slots:
void onConnected() {
qDebug() << "Connected to server!";
socket.write("Hello, server!");
}
void onReadyRead() {
QByteArray data = socket.readAll();
qDebug() << "Server said:" << data;
socket.disconnectFromHost();
}
private:
QTcpSocket socket;
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
TcpClient client("localhost", 12345);
return a.exec();
}
#include "main.moc"代碼解釋
1. 創(chuàng)建 TcpClient 類:這個類繼承自 QObject ,允許使用信號與槽機(jī)制。
2. 連接信號和槽:在構(gòu)造函數(shù)中,將 QTcpSocket 的 connected 和 readyRead 信號分別連接到onConnected 和 onReadyRead 槽。
3. 連接到服務(wù)器:使用 connectToHost() 方法開始連接過程。
4. 處理連接建立:一旦連接建立, onConnected 槽被觸發(fā),客戶端向服務(wù)器發(fā)送一條消息。
5. 接收數(shù)據(jù):當(dāng)數(shù)據(jù)可讀時, onReadyRead 槽被觸發(fā),客戶端讀取并打印來自服務(wù)器的數(shù)據(jù)。
6. 斷開連接:在接收數(shù)據(jù)后,客戶端斷開與服務(wù)器的連接。
這個客戶端示例展示了如何使用 Qt 的信號與槽機(jī)制來處理 TCP 連接。這種方式使得代碼更加清晰,易于維護(hù),并且能更好地處理異步事件。
6 TCP服務(wù)端項(xiàng)目開發(fā)
核心代碼
// 包含主窗口和用戶界面定義
#include "mainwindow.h"
#include "ui_mainwindow.h"
// 主窗口構(gòu)造函數(shù)
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
// 初始化用戶界面
ui->setupUi(this);
// 設(shè)置主窗口中心部件的布局
ui->centralwidget->setLayout(ui->verticalLayout_2);
// 設(shè)置主窗口標(biāo)題
this->setWindowTitle("網(wǎng)絡(luò)調(diào)試助手服務(wù)端-上官Q(mào)T案例");
cursor = ui->textBrowserRev->textCursor(); // 獲取文本瀏覽器的文本光標(biāo)
// 初始時禁用“停止監(jiān)聽”按鈕
ui->pushButtonListenStop->setEnabled(false);
// 創(chuàng)建新的 TCP 服務(wù)器實(shí)例
tcpServer = new QTcpServer(this);
// 將新連接信號連接到處理新連接的槽函數(shù)
connect(tcpServer, SIGNAL(newConnection()), this,
SLOT(mnewConnectionHandler()));
// 獲取系統(tǒng)上所有網(wǎng)絡(luò)接口,并將 IPv4 地址添加到下拉列表中
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();
for (const QNetworkInterface &interface : interfaces) {
for (const QNetworkAddressEntry &entry : interface.addressEntries()) {
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
ui->comboBoxIpAddr->addItem(entry.ip().toString());
}
}
}
}
// 主窗口析構(gòu)函數(shù)
MainWindow::~MainWindow()
{
// 釋放用戶界面資源
delete ui;
}
// “開始監(jiān)聽”按鈕的點(diǎn)擊事件處理函數(shù)
void MainWindow::on_pushButtonListen_clicked()
{
// 偵聽指定 IP 地址和端口
tcpServer->listen(QHostAddress(ui->comboBoxIpAddr->currentText()),
ui->lineEditPort->text().toInt());
// 更新按鈕狀態(tài)
ui->pushButtonListen->setEnabled(false);
ui->pushButtonListenStop->setEnabled(true);
}
// 新 TCP 連接的處理函數(shù)
void MainWindow::mnewConnectionHandler()
{
// 獲取下一個待處理的連接
QTcpSocket *tmpSocket = tcpServer->nextPendingConnection();
// 向文本瀏覽器中添加客戶端信息
ui->textBrowserRev->append("服務(wù)器: 客戶端IP地址是:"+ tmpSocket-
>peerAddress().toString()
+" 客戶端端口號是: "+QString::number(tmpSocket-
>peerPort())+"\n");
// 連接套接字的狀態(tài)變化和數(shù)據(jù)接收信號到相應(yīng)槽函數(shù)
connect(tmpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this, SLOT(mstateChanged(QAbstractSocket::SocketState)));
connect(tmpSocket, SIGNAL(readyRead()), this, SLOT(mreadData()));
}
// 套接字狀態(tài)改變時的槽函數(shù)
void MainWindow::mstateChanged(QAbstractSocket::SocketState state)
{
// 獲取發(fā)送信號的套接字對象
QTcpSocket *tmp = (QTcpSocket *)sender();
// 根據(jù)套接字的不同狀態(tài)進(jìn)行不同處理
switch(state){
case QAbstractSocket::UnconnectedState:
// 客戶端斷開連接
ui->textBrowserRev->append("服務(wù)器:有客戶端斷開連接!");
tmp->deleteLater();
break;
case QAbstractSocket::ConnectedState:
// 客戶端連接
ui->textBrowserRev->append("服務(wù)器:有新客戶端接入!");
break;
default:
break;
}
}
// “停止監(jiān)聽”按鈕的點(diǎn)擊事件處理函數(shù)
void MainWindow::on_pushButtonListenStop_clicked()
{
// 更新按鈕狀態(tài)
ui->pushButtonListen->setEnabled(true);
ui->pushButtonListenStop->setEnabled(true);
// 停止監(jiān)聽端口
tcpServer->close();
}
// 接收到數(shù)據(jù)時的槽函數(shù)
void MainWindow::mreadData()
{
// 獲取發(fā)送信號的套接字對象
QTcpSocket *tmp = (QTcpSocket *)sender();
setTextColor(0,0,0); // 設(shè)置文本顏色為紅色
cursor.insertText("客戶端:"+ tmp->readAll()+"\n");
}
// “發(fā)送”按鈕的點(diǎn)擊事件處理函數(shù)
void MainWindow::on_pushButtonSend_clicked()
{
// 查找所有的子 QTcpSocket 對象
QList<QTcpSocket*> socketList = tcpServer->findChildren<QTcpSocket*>();
// 向每個連接的客戶端發(fā)送數(shù)據(jù)
foreach(QTcpSocket *tmp, socketList){
tmp->write(ui->textEditSnd->toPlainText().toUtf8());
setTextColor(255,0,0); // 設(shè)置文本顏色為紅色
cursor.insertText("服務(wù)端:"+ui->textEditSnd-
>toPlainText().toUtf8()+"\n");
};
}
// 設(shè)置文本顏色的函數(shù)
void MainWindow::setTextColor(int r, int g, int b)
{
QTextCharFormat textFormat;
textFormat.setForeground(QBrush(QColor(r, g, b))); // 根據(jù)提供的 RGB 值設(shè)置顏色
// 應(yīng)用格式到光標(biāo)
cursor.setCharFormat(textFormat);
}
到此這篇關(guān)于基于QT編寫一個網(wǎng)絡(luò)調(diào)試助手的文章就介紹到這了,更多相關(guān)QT網(wǎng)絡(luò)調(diào)試內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言函數(shù)調(diào)用基礎(chǔ)應(yīng)用詳解
函數(shù)就是一段封裝好的,可以重復(fù)使用的代碼,它使得我們的程序更加模塊化,不需要編寫大量重復(fù)的代碼。這篇文章主要介紹了c語言是如何處理函數(shù)調(diào)用的?需要的朋友可以參考下2023-02-02
C++實(shí)現(xiàn)LeetCode(110.平衡二叉樹)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(110.平衡二叉樹),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
C++析構(gòu)函數(shù)內(nèi)部工作機(jī)制詳解
析構(gòu)函數(shù)(Destructor)也是一種特殊的成員函數(shù),沒有返回值,不需要程序員顯式調(diào)用(程序員也沒法顯式調(diào)用),而是在銷毀對象時自動執(zhí)行。構(gòu)造函數(shù)的名字和類名相同,而析構(gòu)函數(shù)的名字是在類名前面加一個~符號2023-02-02

