京东商城官方网站企业网站 开源
作为一名 C++ 开发学习者,掌握 Qt 框架下的网络编程是提升综合开发能力的重要一环。
今天给大家带来 一个基于 TCP 协议的网络调试助手的设计与实现
如果你也在学习 Qt 或者准备做一个类似的网络通信小工具,希望这篇文章能为你提供清晰的开发思路和技术参考。欢迎留言交流你的实现经验!
Part1、TCP网络调试助手
本次设计的网络调试助手在简化界面UI的基础上,重点在于掌握网络通信的核心知识点,尤其是服务器与客户端的创建过程以及数据交互机制。同时,也借此机会复习和巩固了Qt UI控件的使用方法。
1.1、项目整体开发流程

1.2、Qt TCP服务器的关键流程

在创建基于QTcpServer的服务端程序时,需遵循以下关键步骤:
1)、创建并初始化 QTcpServer 实例
- 实例化一个
QTcpServer对象; - 调用
listen()方法监听指定端口上的连接请求。
2)、处理新连接
- 将
newConnection信号连接到对应的槽函数; - 在槽函数中通过
nextPendingConnection()获取QTcpSocket,用于与客户端通信。
3)、读取和发送数据
- 使用
readyRead信号绑定槽函数,以接收来自客户端的数据; - 利用
write()方法将响应数据发送回客户端。
4)、关闭连接
- 在适当的时候调用
close()方法关闭QTcpSocket连接。
示例代码如下:
class MyServer : public QObject {Q_OBJECTpublic: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());QByteArray data = clientSocket->readAll();// 处理收到的数据...}};
⚠️ 注意:在使用 QTcpServer 和 QTcpSocket 时,应妥善处理可能出现的网络错误和异常情况,如连接中断、超时等。
1.3、Qt TCP客户端的关键流程

创建基于 QTcpSocket 的客户端程序主要包括以下几个步骤:
- 创建
QTcpSocket实例 - 连接到服务器
使用
connectToHost()方法连接目标服务器的IP地址和端口号。
- 发送数据到服务器
使用
write()方法发送请求或消息。
- 接收来自服务器的数据
将
readyRead信号绑定到对应的槽函数,用于处理服务器返回的数据。
- 关闭连接
使用
close()方法关闭当前连接。
示例代码如下:
class MyClient : public QObject {Q_OBJECTpublic:MyClient() {QTcpSocket *client = new QTcpSocket(this);connect(client, &QTcpSocket::readyRead, this, &MyClient::onReadyRead);client->connectToHost("server_address", 1234);}private slots:void onReadyRead() {QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());QByteArray data = socket->readAll();// 处理接收到的数据...}};
该客户端尝试连接到指定的服务器地址和端口,并等待服务器返回数据。开发者应根据实际需求合理管理网络错误与异常。
1.4、UI界面的设计
本项目中设计了两个主要界面:
TCP服务端UI界面
包括IP地址选择框、端口输入框、监听按钮、客户端连接状态显示区、数据收发文本框等;

TCP客户端UI界面
包括服务器IP与端口输入框、连接按钮、数据发送与接收框等。

1.5、TCP协议理论知识
以下为TCP协议的基础理论知识,虽在实际编程中由 QTcpSocket 类封装底层细节,但理解其原理对于面试准备及深入学习仍具有重要意义。
TCP协议的基本特点:
特性 | 描述 |
面向连接 | 通信前必须建立连接(三次握手) |
可靠传输 | 数据完整且无误地到达接收方 |
顺序控制 | 确保数据包按序重组 |
流量控制 | 使用滑动窗口机制避免过载 |
拥塞控制 | 动态调整传输速率防止网络拥塞 |
数据分段 | 大块数据自动分片传输 |
确认与重传 | 接收方确认接收,丢失则重传 |
终止连接 | 正常关闭连接(四次挥手) |
TCP连接建立 —— 三次握手

客户端发送SYN报文(同步);
服务器回复SYN-ACK(同步-确认);
客户端发送ACK报文,连接建立完成。
TCP连接终止 —— 四次挥手

一方发送FIN报文(结束);
对方回复ACK确认;
对方发送FIN报文;
原方回复ACK,连接关闭。
Socket的主要类型:
- TCP Socket
:面向连接、可靠;
- UDP Socket
:无连接、不可靠。
Socket的主要功能:
创建网络连接;
监听客户端连接;
发送与接收数据。
Qt中的Socket支持:
QTcpSocket:用于实现TCP通信;
QUdpSocket:用于实现UDP通信。
Socket抽象了网络通信的复杂性,是实现网络通信的重要基础工具之一。
Part2、网络通信核心代码
QTcpServer 是 Qt 网络模块的重要组成部分,用于构建TCP服务器。它可以异步监听客户端连接,并在连接建立后进行数据交换。
2.1、TCP服务端连接的核心代码
在类定义中声明服务器对象:
QTcpServer *server;构造函数中实例化:
server = new QTcpServer(this);点击监听按钮时启动监听并绑定信号:
void Widget::on_btnListen_clicked() {QHostAddress addr("192.168.1.106");quint16 port = 8888;bool ret = server->listen(addr, port);if (!ret) return;connect(server, SIGNAL(newConnection()), this, SLOT(on_newClient_connect()));}
当有客户端接入时获取连接并绑定接收数据信号:
void Widget::on_newClient_connect() {if (server->hasPendingConnections()) {QTcpSocket *tcpSocket = server->nextPendingConnection();qDebug() << "client addr: " << tcpSocket->peerAddress().toString();qDebug() << "client port: " << tcpSocket->peerPort();ui->textEdit_Rev->append("addr: " + tcpSocket->peerAddress().toString());ui->textEdit_Rev->append("port: " + QString::number(tcpSocket->peerPort()));connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(on_readyRead_handler()));ui->comboBox_child->addItem(QString::number(tcpSocket->peerPort()));ui->comboBox_child->setCurrentText(QString::number(tcpSocket->peerPort()));}}
2.2、TCP服务端的数据通信核心代码
当客户端发送数据时触发接收槽函数:
void Widget::on_readyRead_handler() {QTcpSocket *tcpSocket = qobject_cast<QTcpSocket *>(sender());QByteArray revData = tcpSocket->readAll();ui->textEdit_Rev->append("client: " + revData);}
发送按钮槽函数,支持向所有或指定客户端发送数据:
void Widget::on_btnSend_clicked() {QList<QTcpSocket *> clients = server->findChildren<QTcpSocket*>();for (QTcpSocket *temp : clients) {temp->write(ui->textEdit_Send->toPlainText().toStdString().c_str());}}
2.3、TCP客户端的核心代码
客户端对象定义与实例化:
QTcpSocket *client;client = new QTcpSocket(this);
连接按钮槽函数:
void Widget::on_btnConnect_clicked() {QString addr(ui->lineEdit_addr->text());quint16 port = ui->lineEdit_port->text().toInt();client->connectToHost(addr, port);connect(client, SIGNAL(readyRead()), this, SLOT(on_readyRead_handler()));}
数据接收槽函数:
void Widget::on_readyRead_handler() {QByteArray revData = client->readAll();ui->textEdit_rev->append("server: " + revData);}
发送按钮槽函数:
void Widget::on_btnSend_clicked() {QByteArray sendData = ui->textEdit_send->toPlainText().toUtf8();client->write(sendData);}
Part3TCP服务端项目功能优化
3.1、自动刷新IP地址
为了方便用户选择本地IP地址,可以在程序启动时自动扫描系统中可用的IPv4地址并填充至下拉框中:
QList<QHostAddress> addrList = QNetworkInterface::allAddresses();for (QHostAddress addr : addrList) {if (addr.protocol() == QAbstractSocket::IPv4Protocol) {ui->comboBox_Addr->addItem(addr.toString());}}
3.2、服务器向不同客户端发数据
为实现向不同客户端单独发送数据的功能,可自定义一个继承于 QComboBox 的 myComboBox 类,并重写鼠标事件以触发自定义信号:
自定义类实现:
class myComboBox : public QComboBox {Q_OBJECTprotected:void mousePressEvent(QMouseEvent *e) override;signals:void on_ComboBox_clicked();};void myComboBox::mousePressEvent(QMouseEvent *e) {if (e->button() == Qt::LeftButton)emit on_ComboBox_clicked();QComboBox::mousePressEvent(e);}
主界面中绑定信号与槽:
connect(ui->comboBox_child, &myComboBox::on_ComboBox_clicked, this, &Widget::on_refresh_comboBox);刷新选项框内容:
void Widget::on_refresh_comboBox() {ui->comboBox_child->clear();QList<QTcpSocket*> clients = server->findChildren<QTcpSocket*>();for (auto client : clients) {ui->comboBox_child->addItem(QString::number(client->peerPort()));}ui->comboBox_child->addItem("all");}
数据发送优化逻辑:
void Widget::on_btnSend_clicked() {QList<QTcpSocket *> clients = server->findChildren<QTcpSocket*>();if (clients.isEmpty()) return;QString target = ui->comboBox_child->currentText();if (target != "all") {for (auto client : clients) {if (QString::number(client->peerPort()) == target) {client->write(ui->textEdit_Send->toPlainText().toStdString().c_str());}}} else {for (auto client : clients) {client->write(ui->textEdit_Send->toPlainText().toStdString().c_str());}}ui->textEdit_Rev->moveCursor(QTextCursor::End);ui->textEdit_Rev->ensureCursorVisible();}
3.3、TextEdit 设置特定位置文字颜色
为了在 QTextEdit 控件中设置特定位置的文字颜色,需要通过光标级别的操作来实现。Qt 提供了 textCursor() 方法获取当前光标对象,并结合 setCharFormat() 实现字符格式的定制。
函数原型与嵌套关系如下:
QTextCursor: QTextEdit::textCursor() constQTextCursor: void QTextCursor::setCharFormat(const QTextCharFormat &format) //方法QTextCharFormat: void setForeground(const QBrush &brush) //方法QBrush: QBrush(const QColor &color, const QPixmap &pixmap) //构造函数QColor: QColor(const QColor &color) //构造函数将该功能封装为一个函数,参数分别为字体颜色和待显示文本:
void Widget::setInsertColor(Qt::GlobalColor color, QString str){// 获取当前光标位置QTextCursor cursor = ui->textEdit_rev->textCursor();QTextCharFormat format;// 设置字符前景色format.setForeground(QColor(color));cursor.setCharFormat(format);// 插入带颜色的文本并换行cursor.insertText(str + "\n");}
3.4、客户端断开检测
当客户端主动断开连接时,服务器会接收到 disconnected() 信号。通过绑定该信号与槽函数,可以及时检测到客户端的断开行为。
绑定 disconnected() 信号:
connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(on_disconnected()));客户端断开连接槽函数实现:
在槽函数中完成以下操作:
在文本框中提示客户端已退出;
从下拉框中移除对应客户端的端口号;
删除客户端对象;
判断是否仍有连接中的客户端,决定是否禁用发送按钮。
void Widget::on_disconnected(){QTcpSocket *tcpSocket = qobject_cast<QTcpSocket *>(sender());ui->textEdit_Rev->append("client quit!");// 查找并移除选项框中对应的端口号int tempIdx = ui->comboBox_child->findText(QString::number(tcpSocket->peerPort()));if (tempIdx != -1) {ui->comboBox_child->removeItem(tempIdx);}// 删除客户端对象tcpSocket->deleteLater();// 若无其他客户端,禁用发送按钮if (server->findChildren<QTcpSocket*>().isEmpty()) {ui->btnSend->setEnabled(false);}}
3.5、停止监听的实现
点击“停止监听”按钮后,需关闭所有已连接的客户端,并关闭服务器本身。
槽函数实现如下:
void Widget::on_btnStopListen_clicked(){// 获取所有已连接的客户端QList<QTcpSocket *> clients = server->findChildren<QTcpSocket*>();for (QTcpSocket *temp : clients) {temp->close(); // 关闭连接}server->close(); // 关闭服务器}
Part4、TCP客户端项目开发及优化
4.1、检测连接状态
客户端成功连接服务器时会发出 connected() 信号,若连接失败则会发出 error() 信号。但由于错误信号响应存在延迟,因此可使用定时器机制判断是否超时。
构造函数中初始化定时器:
// 初始化定时器timer = new QTimer(this);timer->setSingleShot(true); // 单次触发timer->setInterval(3000); // 超时时间为3秒// 启动定时器timer->start();
绑定信号与槽函数:
connect(client, SIGNAL(connected()), this, SLOT(on_connected()));connect(timer, SIGNAL(timeout()), this, SLOT(on_timer_out()));
连接成功槽函数实现:
void Widget::on_connected(){timer->stop(); // 停止定时器ui->textEdit_rev->append("连接成功");// 更新控件状态ui->btnDisconnect->setEnabled(true);ui->btnSend->setEnabled(true);ui->lineEdit_addr->setEnabled(false);ui->lineEdit_port->setEnabled(false);ui->btnConnect->setEnabled(false);this->setEnabled(true);// 光标定位至末尾ui->textEdit_rev->moveCursor(QTextCursor::End);ui->textEdit_rev->ensureCursorVisible();}
连接超时槽函数实现:
void Widget::on_timer_out(){ui->textEdit_rev->append("连接超时");client->abort(); // 中止连接this->setEnabled(true);on_btnDisconnect_clicked(); // 手动调用断开连接}
4.2、其他细节功能
文本框特定颜色分区
与服务器端相同,使用自定义函数实现带颜色的文本插入:
void Widget::setInsertColor(Qt::GlobalColor color, QString str){QTextCursor cursor = ui->textEdit_rev->textCursor();QTextCharFormat format;format.setForeground(QColor(color));cursor.setCharFormat(format);cursor.insertText(str + "\n");}
控件使能与失能控制
通过 setEnabled() 方法控制控件的可用状态:
ui->btnDisconnect->setEnabled(false);ui->btnSend->setEnabled(false);
文本框自动滚动到底部
确保每次插入新内容后,光标自动定位至最后一行:
ui->textEdit_rev->moveCursor(QTextCursor::End);ui->textEdit_rev->ensureCursorVisible();
Part5、总结
主要知识点总结如下:
TCPServer 类相关 API 与常用信号
功能 | API |
创建服务器 | new QTcpServer(this) |
监听端口 | server->listen(addr, port) |
获取客户端 | nextPendingConnection() |
关闭连接 | close() |
常用信号 | 触发条件 |
newConnection() | 有新客户端连接时触发 |
QTcpSocket 类常用 API 与信号
功能 | API |
连接服务器 | connectToHost(addr, port) |
发送数据 | write(data) |
接收数据 | readAll() |
断开连接 | disconnectFromHost() 或 abort() |
常用信号 | 触发条件 |
readyRead() | 收到数据时触发 |
connected() | 成功连接服务器时触发 |
disconnected() | 客户端断开连接时触发 |
error() | 连接或通信过程中发生错误时触发 |
QTextEdit 内容读取与写入方法
操作 | 方法 |
读取全部内容 | toPlainText() |
插入带格式文本 | insertText() + setCharFormat() |
移动光标到底部 | moveCursor(QTextCursor::End) |
点击下方关注【Linux教程】,获取编程学习路线、项目教程、简历模板、大厂面试题pdf文档、大厂面经、编程交流圈子等等。
