一、實驗效果演示
實現 Qt 的 TCP 客戶端,實現和服務器通信
二、代碼框架
三、代碼
tcpclient客戶端代碼
tcpclient.cpp
#include "tcpclient.h"
#include "ui_tcpclient.h"
#include <QDebug>
#include <QMessageBox>
#include <QRegExpValidator>// 構造函數:初始化TCP客戶端界面和成員變量
tcpclient::tcpclient(QWidget *parent): QWidget(parent) // 調用父類QWidget的構造函數, ui(new Ui::tcpclient) // 初始化UI對象, mSocket(nullptr) // 初始化TCP套接字指針為nullptr
{ui->setupUi(this); // 初始化UI界面setWindowTitle("TCP Client"); // 設置窗口標題為"TCP Client"// 初始狀態下禁用發送功能(未連接服務器時不能發送數據)ui->lineEdit_send->setEnabled(false); // 禁用發送內容輸入框ui->pushButton_send->setEnabled(false); // 禁用發送按鈕// 設置IP地址驗證器,確保輸入的IP地址格式正確// 創建正則表達式驗證器,用于驗證IPv4地址格式QRegExpValidator *ipValidator = new QRegExpValidator(// 正則表達式:匹配xxx.xxx.xxx.xxx格式的IPv4地址,每個xxx為0-255QRegExp("\\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\b"),this // 父對象為當前窗口,確保內存自動管理);ui->lineEdit_ip->setValidator(ipValidator); // 為IP輸入框設置驗證器ui->lineEdit_ip->setText("192.168.53.128"); // 設置默認IP地址
}// 析構函數:釋放資源
tcpclient::~tcpclient()
{// 如果套接字存在且處于連接狀態,斷開與服務器的連接if (mSocket && mSocket->state() == QAbstractSocket::ConnectedState) {mSocket->disconnectFromHost();}delete ui; // 釋放UI對象
}// "連接/斷開"按鈕點擊事件處理函數
void tcpclient::on_pushButton_connect_clicked()
{// 如果套接字未初始化,創建QTcpSocket對象if (!mSocket) {mSocket = new QTcpSocket(this); // 創建TCP套接字,父對象為當前窗口// 連接信號與槽函數,處理各種網絡事件// 當連接成功建立時,觸發onConnected函數connect(mSocket, &QTcpSocket::connected, this, &tcpclient::onConnected);// 當連接斷開時,觸發onDisconnected函數connect(mSocket, &QTcpSocket::disconnected, this, &tcpclient::onDisconnected);// 當有數據可讀時,觸發onReadyRead函數connect(mSocket, &QTcpSocket::readyRead, this, &tcpclient::onReadyRead);// 當發生錯誤時,觸發onErrorOccurred函數(兼容舊版本Qt)connect(mSocket, QOverload<QAbstractSocket::SocketError>::of(&QTcpSocket::error), this, &tcpclient::onErrorOccurred); }// 檢查當前連接狀態if (mSocket->state() == QAbstractSocket::ConnectedState) {// 如果已連接,則斷開連接mSocket->disconnectFromHost();} else {// 未連接狀態,嘗試連接到服務器QString ip = ui->lineEdit_ip->text(); // 獲取輸入的IP地址quint16 port = ui->spinBox_port->value(); // 獲取輸入的端口號// 檢查IP地址是否為空if (ip.isEmpty()) {QMessageBox::warning(this, "警告", "請輸入服務器IP地址"); // 彈出警告對話框return; // 退出函數,不執行連接操作}// 連接到指定的IP地址和端口號mSocket->connectToHost(QHostAddress(ip), port);// 在消息顯示區添加連接狀態信息ui->textBrowser->insertPlainText("正在連接到 " + ip + ":" + QString::number(port) + "...\n");}
}// "發送"按鈕點擊事件處理函數
void tcpclient::on_pushButton_send_clicked()
{// 檢查套接字是否存在且處于連接狀態if (!mSocket || mSocket->state() != QAbstractSocket::ConnectedState) {return; // 不滿足條件則退出函數}// 獲取要發送的數據QString str = ui->lineEdit_send->text();// 檢查輸入內容是否為空if (str.isEmpty()) {return; // 內容為空則不發送}// 發送數據:將QString轉換為UTF-8編碼的字節數組mSocket->write(str.toUtf8());// 在界面上顯示發送的內容,前綴"snd:"標識為發送的數據ui->textBrowser->insertPlainText("snd: " + str + "\n");// 清空發送輸入框ui->lineEdit_send->clear();
}// 連接成功建立時的處理函數
void tcpclient::onConnected()
{// 在消息顯示區添加連接成功信息ui->textBrowser->insertPlainText("已連接到服務器\n");// 將"連接"按鈕文本改為"斷開連接"ui->pushButton_connect->setText("斷開連接");// 啟用發送功能(連接成功后才能發送數據)ui->lineEdit_send->setEnabled(true); // 啟用發送內容輸入框ui->pushButton_send->setEnabled(true); // 啟用發送按鈕// 禁用IP和端口編輯(連接建立后不允許修改連接參數)ui->lineEdit_ip->setEnabled(false); // 禁用IP輸入框ui->spinBox_port->setEnabled(false); // 禁用端口選擇框
}// 連接斷開時的處理函數
void tcpclient::onDisconnected()
{// 在消息顯示區添加斷開連接信息ui->textBrowser->insertPlainText("已與服務器斷開連接\n");// 將"斷開連接"按鈕文本改回"連接"ui->pushButton_connect->setText("連接");// 禁用發送功能(斷開連接后不能發送數據)ui->lineEdit_send->setEnabled(false); // 禁用發送內容輸入框ui->pushButton_send->setEnabled(false); // 禁用發送按鈕// 啟用IP和端口編輯(斷開連接后允許修改連接參數)ui->lineEdit_ip->setEnabled(true); // 啟用IP輸入框ui->spinBox_port->setEnabled(true); // 啟用端口選擇框
}// 有數據可讀時的處理函數
void tcpclient::onReadyRead()
{// 檢查套接字是否存在if (!mSocket) {return; // 套接字不存在則退出}// 讀取所有接收到的數據QByteArray data = mSocket->readAll();// 在界面上顯示接收的內容,前綴"rcv:"標識為接收的數據ui->textBrowser->insertPlainText("rcv: " + QString(data) + "\n");
}// 網絡錯誤發生時的處理函數
void tcpclient::onErrorOccurred(QAbstractSocket::SocketError error)
{Q_UNUSED(error); // 標記error參數未使用,避免編譯警告// 檢查套接字是否存在if (mSocket) {// 在消息顯示區添加錯誤信息ui->textBrowser->insertPlainText("錯誤: " + mSocket->errorString() + "\n");}
}
tcpclient.h
#ifndef TCPCLIENT_H // 防止頭文件被重復包含的宏定義
#define TCPCLIENT_H // 定義頭文件宏#include <QWidget> // 包含QWidget類,tcpclient類繼承自QWidget
#include <QTcpSocket> // 包含QTcpSocket類,用于TCP通信
#include <QAbstractSocket> // 包含QAbstractSocket類,提供套接字的基本功能和枚舉
#include <QRegExpValidator> // 包含QRegExpValidator類,用于正則表達式驗證
#include <QMessageBox> // 包含QMessageBox類,用于顯示消息對話框
#include <QHostAddress> // 包含QHostAddress類,用于處理IP地址QT_BEGIN_NAMESPACE // Qt命名空間開始
namespace Ui { class tcpclient; } // 聲明Ui命名空間中的tcpclient類(由.ui文件生成)
QT_END_NAMESPACE // Qt命名空間結束// 定義tcpclient類,繼承自QWidget,用于實現TCP客戶端功能
class tcpclient : public QWidget
{Q_OBJECT // 啟用Qt的元對象系統,支持信號和槽機制public:// 構造函數:創建tcpclient對象,parent為父窗口指針,默認為nullptrtcpclient(QWidget *parent = nullptr);// 析構函數:釋放tcpclient對象占用的資源~tcpclient();private slots: // 私有槽函數,用于處理各種事件// 處理"連接/斷開"按鈕的點擊事件void on_pushButton_connect_clicked();// 處理"發送"按鈕的點擊事件void on_pushButton_send_clicked();// 處理連接成功建立的事件void onConnected();// 處理連接斷開的事件void onDisconnected();// 處理有數據可讀的事件void onReadyRead();// 處理網絡錯誤發生的事件,參數為錯誤類型void onErrorOccurred(QAbstractSocket::SocketError error);private: // 私有成員變量Ui::tcpclient *ui; // 指向UI界面對象的指針,用于訪問界面控件QTcpSocket *mSocket; // 指向QTcpSocket對象的指針,用于TCP通信
};#endif // TCPCLIENT_H // 結束頭文件宏定義
tcpclient.pro
QT += core gui
QT += network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0SOURCES += \main.cpp \tcpclient.cppHEADERS += \tcpclient.hFORMS += \tcpclient.ui# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
main.cpp
#include "tcpclient.h" // 包含tcpclient類的頭文件,用于創建客戶端窗口對象#include <QApplication> // 包含QApplication類的頭文件,Qt應用程序的核心類// 程序入口函數,argc是命令行參數數量,argv是命令行參數數組
int main(int argc, char *argv[])
{// 創建QApplication對象a,初始化Qt應用程序,處理命令行參數QApplication a(argc, argv);// 創建tcpclient類的實例w,這是客戶端的主窗口對象tcpclient w;// 顯示主窗口w,此時窗口才會在屏幕上可見w.show();// 進入Qt應用程序的事件循環,等待用戶交互(如點擊按鈕、輸入文本等)// 函數返回應用程序退出時的狀態碼return a.exec();
}
tcpclient.ui(8個組件)
-
QT設計師界面UI 設計建議:
客戶端 UI 應包含以下元素:- IP 地址輸入框(lineEdit_ip)
- 端口號選擇框(spinBox_port)
- 連接 / 斷開按鈕(pushButton_connect)
- 發送內容輸入框(lineEdit_send)
- 發送按鈕(pushButton_send)
- 消息顯示區域(textBrowser)
6TcpServer服務器端
tcpsever.cpp
#include "tcpserver.h"
#include "ui_tcpserver.h"
#include <QDebug>TcpServer::TcpServer(QWidget *parent): QWidget(parent), ui(new Ui::TcpServer)
{ui->setupUi(this);
}TcpServer::~TcpServer()
{delete ui;
}void TcpServer::on_pushButton_start_clicked()
{//1.構建QTcpServer對象mServer = new QTcpServer(this);//連接有客戶端連接上來的信號QObject::connect(mServer,&QTcpServer::newConnection,this,[&](){//獲取客戶端連接上來的套接字mSocket = mServer->nextPendingConnection();//獲取客戶端的地址QHostAddress addr = mSocket->peerAddress();qDebug()<<addr.toString()<<"連接上來!";//使能發送ui->lineEdit->setEnabled(true);ui->pushButton_send->setEnabled(true);//連接readyRead信號和槽QObject::connect(mSocket,&QTcpSocket::readyRead,this,[&](){//接收數據QByteArray arr = mSocket->readAll();//轉換成字符串并顯示QString str(arr);//顯示ui->textBrowser->insertPlainText("rcv:"+str+"\n");});});//2.監聽mServer->listen(QHostAddress::Any,ui->spinBox->value());//禁止修改ui->spinBox->setEnabled(false);ui->pushButton_start->setEnabled(false);
}//發送
void TcpServer::on_pushButton_send_clicked()
{//獲取發送的數據QString str = ui->lineEdit->text();//轉換成QByteArrayQByteArray arr;arr.append(str);//發送mSocket->write(arr);//顯示要發送的內容ui->textBrowser->insertPlainText("snd:"+str+"\n");//清空ui->lineEdit->clear();
}
main.cpp
#include "tcpserver.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv);TcpServer w;w.show();return a.exec();
}
tcpserver.h
#ifndef TCPSERVER_H
#define TCPSERVER_H#include <QWidget>
#include <QtNetwork>QT_BEGIN_NAMESPACE
namespace Ui { class TcpServer; }
QT_END_NAMESPACEclass TcpServer : public QWidget
{Q_OBJECTpublic:TcpServer(QWidget *parent = nullptr);~TcpServer();private slots:void on_pushButton_start_clicked();void on_pushButton_send_clicked();private:Ui::TcpServer *ui;//TCP服務器QTcpServer *mServer;QTcpSocket *mSocket;
};
#endif // TCPSERVER_H
6TcpServer.pro
QT += core gui networkgreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0SOURCES += \main.cpp \tcpserver.cppHEADERS += \tcpserver.hFORMS += \tcpserver.ui# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target