Qt TCP 网络通信详解(笔记)
本文重点讲解
QTcpSocket与QTcpServer的使用逻辑、信号槽机制与完整实现流程。
适用于聊天程序、文件传输、远程控制等基础 TCP 通信场景。
一、Qt 网络模块简介
Qt 的网络功能集中在模块 QtNetwork 中。
要使用 TCP 功能,必须在 .pro 或 CMakeLists.txt 中引入该模块。 CMakeLists.txt 示例
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Network)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Network)target_link_libraries(target_name PRIVATE Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Network)
二、核心类说明
Qt 的 TCP 通信基于 QTcpServer(服务端)和 QTcpSocket(客户端/连接端)两大类。
| 类名 | 功能 |
|---|---|
| QTcpServer | 监听端口、等待客户端连接 |
| QTcpSocket | 负责数据传输(客户端与每个连接的会话) |
| QHostAddress | 表示 IP 地址 |
| QNetworkInterface | 查询本机网卡地址 |
| QAbstractSocket | TCP/UDP 套接字基类 |
三、通信原理
通信流程如下:
┌────────────────┐ ┌───────────────────┐
│ QTcpServer │ │ QTcpSocket │
│ (服务端) │◀──────▶│ (客户端/连接) │
└────────────────┘ └───────────────────┘↑ newConnection() ││ │└── nextPendingConnection()││readyRead() -> readAll()
服务端通过
listen()开启端口监听;有客户端连接时,会触发
newConnection()信号;使用
nextPendingConnection()获取一个新的QTcpSocket;客户端使用
connectToHost()连接服务器;通信时通过信号
readyRead()读取数据,通过write()发送。
四、服务端实现
widget.h
#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>QT_BEGIN_NAMESPACE
namespace Ui {
class ServerWidget;
}
QT_END_NAMESPACEclass ServerWidget : public QWidget
{Q_OBJECTpublic:explicit ServerWidget(QWidget *parent = nullptr);~ServerWidget();private slots:void onNewConnection(); // 客户端连接void onReadyRead(); // 客户端发送消息private:Ui::ServerWidget *ui;QTcpServer *server; // 监听对象QTcpSocket *socket; // 通信套接字
};#endif // SERVERWIDGET_H
widget.cpp
#include "serverwidget.h"
#include "ui_serverwidget.h"
#include <QMessageBox>
#include <QDebug>ServerWidget::ServerWidget(QWidget *parent): QWidget(parent), ui(new Ui::ServerWidget), server(new QTcpServer(this))
{ui->setupUi(this);// 开启监听if (!server->listen(QHostAddress::AnyIPv4, 8000)) {QMessageBox::critical(this, "错误", "监听失败:" + server->errorString());return;}connect(server, &QTcpServer::newConnection, this, &ServerWidget::onNewConnection);qDebug() << "服务器启动成功,监听端口 8000";
}ServerWidget::~ServerWidget() { delete ui; }void ServerWidget::onNewConnection()
{socket = server->nextPendingConnection();connect(socket, &QTcpSocket::readyRead, this, &ServerWidget::onReadyRead);QString ip = socket->peerAddress().toString();quint16 port = socket->peerPort();ui->logBrowser->append(QString("客户端连接:%1:%2").arg(ip).arg(port));socket->write("连接成功,欢迎来到服务器!\n");
}void ServerWidget::onReadyRead()
{QByteArray data = socket->readAll();QString msg = QString::fromUtf8(data);ui->logBrowser->append("收到客户端:" + msg);socket->write("服务器已收到: " + data);
}
五、客户端实现
client.h
#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H#include <QWidget>
#include <QTcpSocket>QT_BEGIN_NAMESPACE
namespace Ui {
class ClientWidget;
}
QT_END_NAMESPACEclass ClientWidget : public QWidget
{Q_OBJECTpublic:explicit ClientWidget(QWidget *parent = nullptr);~ClientWidget();private slots:void onConnectClicked(); // 连接服务器void onSendClicked(); // 发送消息void onReadyRead(); // 接收服务器消息private:Ui::ClientWidget *ui;QTcpSocket *socket;
};#endif // CLIENTWIDGET_H
client.cpp
#include "clientwidget.h"
#include "ui_clientwidget.h"
#include <QHostAddress>
#include <QMessageBox>ClientWidget::ClientWidget(QWidget *parent): QWidget(parent), ui(new Ui::ClientWidget), socket(new QTcpSocket(this))
{ui->setupUi(this);connect(socket, &QTcpSocket::readyRead, this, &ClientWidget::onReadyRead);connect(ui->btnConnect, &QPushButton::clicked, this, &ClientWidget::onConnectClicked);connect(ui->btnSend, &QPushButton::clicked, this, &ClientWidget::onSendClicked);
}ClientWidget::~ClientWidget() { delete ui; }void ClientWidget::onConnectClicked()
{QString ip = ui->ipEdit->text();quint16 port = ui->portEdit->text().toUShort();socket->connectToHost(QHostAddress(ip), port);if (socket->waitForConnected(2000)) {QMessageBox::information(this, "成功", "连接服务器成功!");} else {QMessageBox::critical(this, "失败", "连接超时");}
}void ClientWidget::onSendClicked()
{QString msg = ui->msgEdit->text();socket->write(msg.toUtf8());ui->msgEdit->clear();
}void ClientWidget::onReadyRead()
{QByteArray data = socket->readAll();ui->recvBrowser->append("服务器:" + QString::fromUtf8(data));
}
六、常用信号槽总结
| 信号 | 含义 |
|---|---|
QTcpServer::newConnection() | 当有新的客户端连接时触发 |
QTcpSocket::readyRead() | 当有数据可读时触发 |
QTcpSocket::connected() | 连接成功后触发 |
QTcpSocket::disconnected() | 连接断开时触发 |
QTcpSocket::errorOccurred() | 网络错误时触发 |
七、常见问题与技巧
| 问题 | 原因 | 解决办法 |
|---|---|---|
| 客户端连接不上 | 监听 IP/端口错误、防火墙拦截 | 使用 127.0.0.1 测试 |
| readyRead() 不触发 | 服务器未 connect() 信号槽 | 检查 connect 语句 |
| 中文乱码 | 字符编码不一致 | 使用 QString::fromUtf8() 转换 |
| 多客户端管理 | 每个连接都创建独立 QTcpSocket | 存入 QList<QTcpSocket*> |
| UI 卡顿 | 长时间操作在主线程中执行 | 用 QThread 或 QTimer::singleShot() |
八、消息交互逻辑总结
[客户端] connectToHost() → 发送连接请求↓
[服务端] newConnection() → 获取 socket↓
[服务端] readyRead() → 接收消息并反馈↓
[客户端] readyRead() → 显示服务器返回
Qt 的网络模块基于 事件循环,通信全程异步进行,不会阻塞 UI。
九、快速调试建议
本地测试使用:
IP: 127.0.0.1 Port: 8000服务端启动 → 客户端再连接;
若连接失败,可使用命令:
netstat -ano | find "8000"确认端口监听是否成功;
建议开启 Qt 的调试输出:
qDebug() << socket->state();
十、总结
| 功能 | 使用类 | 触发信号 | 典型函数 |
|---|---|---|---|
| 监听连接 | QTcpServer | newConnection() | listen() |
| 获取连接 | QTcpServer | — | nextPendingConnection() |
| 连接服务器 | QTcpSocket | connected() | connectToHost() |
| 发送消息 | QTcpSocket | — | write() |
| 接收消息 | QTcpSocket | readyRead() | readAll() |
总结一句话:
QTcpServer管理连接,QTcpSocket管理通信。
Qt 的 TCP 模型是“信号槽 + 异步事件循环”,
不需要多线程,也能高效、优雅地完成网络交互。
