作为一名 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文档、大厂面经、编程交流圈子等等。