3. TCP多客户端并发处理
任务目标
实现一个基于 QTcpServer 的并发TCP服务器,能够同时接受多个客户端连接,并为每个连接独立收发数据。服务器采用“每连接一线程”模型,确保一个客户端的阻塞操作不影响其他客户端的通信。
实现方案
使用 QTcpServer::incomingConnection() 的默认机制:每当有新连接时,Qt 会自动创建对应的 QTcpSocket 并发出 newConnection() 信号。在槽函数中获取已连接的 socket,将其移入一个 QThread 工作线程,并在该线程中执行独立的收发逻辑。主线程仅负责监听和分发连接,不参与具体的数据通信。
完整代码
// server.h
#ifndef SERVER_H
#define SERVER_H
#include <QTcpServer>
#include <QThread>
#include <QTcpSocket>
class Server : public QTcpServer
{
Q_OBJECT
public:
explicit Server(QObject *parent = nullptr);
protected:
void incomingConnection(qintptr socketDescriptor) override;
};
// 每个连接的工作线程
class ClientWorker : public QThread
{
Q_OBJECT
public:
explicit ClientWorker(qintptr socketDescriptor, QObject *parent = nullptr);
void run() override;
private:
qintptr m_socketDescriptor;
};
#endif // SERVER_H
// server.cpp
#include "server.h"
#include <QDataStream>
#include <QDebug>
Server::Server(QObject *parent) : QTcpServer(parent) {}
void Server::incomingConnection(qintptr socketDescriptor)
{
// 为每个连接创建独立线程
ClientWorker *worker = new ClientWorker(socketDescriptor);
connect(worker, &QThread::finished, worker, &QObject::deleteLater);
worker->start();
}
ClientWorker::ClientWorker(qintptr socketDescriptor, QObject *parent)
: QThread(parent), m_socketDescriptor(socketDescriptor) {}
void ClientWorker::run()
{
QTcpSocket socket;
if (!socket.setSocketDescriptor(m_socketDescriptor)) {
qWarning() << "Failed to set socket descriptor";
return;
}
// 向客户端发送欢迎消息
socket.write("Welcome! Send 'quit' to disconnect.\n");
socket.flush();
// 独立收发循环
while (socket.state() == QAbstractSocket::ConnectedState) {
if (!socket.waitForReadyRead(100)) {
continue; // 超时继续检查连接状态
}
while (socket.bytesAvailable() > 0) {
QByteArray data = socket.readAll();
QString message = QString::fromUtf8(data).trimmed();
if (message == "quit") {
socket.write("Bye!\n");
socket.flush();
socket.disconnectFromHost();
return;
}
// 回显收到的消息
QString response = "Echo: " + message + "\n";
socket.write(response.toUtf8());
socket.flush();
}
}
}
// main.cpp
#include <QCoreApplication>
#include "server.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Server server;
if (!server.listen(QHostAddress::Any, 8080)) {
qCritical() << "Server failed to listen:" << server.errorString();
return 1;
}
qInfo() << "Server listening on port 8080...";
return a.exec();
}
项目文件(.pro)
QT += core network
QT -= gui
TARGET = tcp_server
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp server.cpp
HEADERS += server.h
运行与验证
编译运行:
qmake
make
./tcp_server
验证步骤:
- 启动服务器,观察终端输出
Server listening on port 8080...。 - 打开多个终端(至少2个),分别使用
nc localhost 8080或telnet localhost 8080连接服务器。 - 每个客户端连接后应立即收到欢迎消息
Welcome! Send 'quit' to disconnect. - 在任意客户端输入文本并回车,服务器应返回
Echo: 输入的文本。 - 在其中一个客户端输入
quit,该客户端收到Bye!后断开;此时其他客户端仍可正常收发消息,不受影响。
通过条件:上述步骤全部成功执行,且多个客户端同时在线时消息互不干扰,即证明服务器实现了并发处理。
关键说明
- 本实现使用
waitForReadyRead()配合超时实现非阻塞式轮询,避免线程永久阻塞无法退出。 - 每个连接独立线程,线程数随客户端数量增长。对于高并发场景,可改用Qt线程池(
QtConcurrent)或事件循环+状态机模型,但本案例聚焦于“每连接一线程”的直观实现。 - 生产环境中建议限制最大连接数,防止资源耗尽。