在使用 Qt 进行网络通信开发时,TCP 是最常用、最可靠的协议之一。Qt 提供了丰富的类来支持 TCP 通信,其中最关键的两个类就是:
QTcpServer
:用于监听客户端连接的 TCP 服务端类;QTcpSocket
:用于发送和接收数据的通信类。
本篇文章将围绕这两个类展开,讲清楚它们的接口、作用、使用流程以及开发中的注意事项。
一、QTcpServer:TCP 服务监听类
✦ 类功能概述
QTcpServer
是 TCP 通信中的“门卫”,负责监听指定端口,一旦有客户端发起连接请求,它就会通知你,并把连接交给你处理。
✦ 常用接口函数
函数 | 功能说明 |
---|---|
bool listen(const QHostAddress &address, quint16 port) | 启动监听 |
void close() | 停止监听并释放资源 |
bool isListening() const | 查询当前是否处于监听状态 |
QTcpSocket *nextPendingConnection() | 获取一个新的客户端连接对象 |
✦ 重要信号
信号 | 发出时机 |
---|---|
void newConnection() | 有客户端连接到达时发出 |
void acceptError(QAbstractSocket::SocketError) | 接收连接出错时发出 |
✦ 示例代码:启动监听
QTcpServer *server = new QTcpServer(this);
// 启动监听任意地址上的 12345 端口
server->listen(QHostAddress::Any, 12345);
// 连接信号处理新连接
connect(server, &QTcpServer::newConnection, this, [=]() {
QTcpSocket *clientSocket = server->nextPendingConnection();
qDebug() << "客户端连接:" << clientSocket->peerAddress().toString();
});
二、QTcpSocket:TCP 通信类
✦ 类功能概述
QTcpSocket
是 TCP 通信的“通道”。你可以用它主动连接服务器,也可以用它处理客户端请求,并实现数据的收发。服务器端每个客户端连接都会生成一个独立的 QTcpSocket
实例。
✦ 常用接口函数
函数 | 功能说明 |
---|---|
void connectToHost(const QString &hostName, quint16 port) | 连接服务器(用于客户端) |
void disconnectFromHost() | 主动断开连接 |
qint64 write(const QByteArray &data) | 发送数据 |
QByteArray readAll() | 读取所有收到的数据 |
QString peerAddress().toString() | 获取对方 IP 地址 |
quint16 peerPort() | 获取对方端口号 |
✦ 重要信号
信号 | 触发条件 |
---|---|
void readyRead() | 有新数据可读 |
void connected() | 成功建立连接(客户端使用) |
void disconnected() | 对方断开连接 |
void errorOccurred(QAbstractSocket::SocketError) | 发生通信错误 |
✦ 示例代码:接收并响应数据
connect(clientSocket, &QTcpSocket::readyRead, this, [=]() {
QByteArray data = clientSocket->readAll();
qDebug() << "收到数据:" << data;
clientSocket->write("服务器已收到\n");
});
三、典型通信流程总结(服务端)
一个完整的服务端通信流程如下:
- 使用
QTcpServer::listen()
启动监听; - 捕捉
newConnection()
信号,调用nextPendingConnection()
获取QTcpSocket
; - 通过
readyRead
信号读取客户端数据; - 使用
write()
方法发送数据; - 在
disconnected
信号中释放资源。
✅ 重点记忆:服务器只监听一次,但每个客户端连接都对应一个独立的 QTcpSocket。
四、开发中的注意事项
✦ 多客户端连接管理
每个连接都需要单独管理。通常做法是将每个连接保存到一个容器中,比如:
QList<QTcpSocket*> clientList;
客户端断开时记得从列表中移除并释放资源:
connect(socket, &QTcpSocket::disconnected, [=]() {
clientList.removeOne(socket);
socket->deleteLater();
});
✦ socket 生命周期管理
- 一定不要直接 delete socket 对象,应使用
deleteLater()
; - 不释放资源可能导致内存泄漏。
✦ 与界面线程配合使用
默认情况下,QTcpSocket
应该在其创建的线程中使用。如果你想在子线程中处理连接,必须使用 socket->moveToThread()
,并通过信号槽与主线程通信,避免跨线程操作对象。
五、下篇预告
下一篇文章中,我们将基于这套机制构建一个无界面的 TCP 服务程序,支持定时收发消息,实现真正可运行的后台 TCP 服务应用。欢迎继续关注!