本文重点讲解
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
cpp
#ifndef SERVERWIDGET_H
#define SERVERWIDGET_H
#include <QWidget>
#include <QTcpServer>
#include <QTcpSocket>
QT_BEGIN_NAMESPACE
namespace Ui {
class ServerWidget;
}
QT_END_NAMESPACE
class ServerWidget : public QWidget
{
Q_OBJECT
public:
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
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
cpp
#ifndef CLIENTWIDGET_H
#define CLIENTWIDGET_H
#include <QWidget>
#include <QTcpSocket>
QT_BEGIN_NAMESPACE
namespace Ui {
class ClientWidget;
}
QT_END_NAMESPACE
class ClientWidget : public QWidget
{
Q_OBJECT
public:
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
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 模型是"信号槽 + 异步事件循环",
不需要多线程,也能高效、优雅地完成网络交互。