// 实现目标:每个client连接上来,生成一个子线程来处理。
CMAKE:
find_package(Qt5 COMPONENTS Core Widgets Network REQUIRED)
target_link_libraries(demo1 PRIVATE Qt5::Widgets Qt5::Network)
//clientScoketHandle.h 客户端连接处理
class clientSocketHandle : public QObject
{
Q_OBJECT
public:
explicit clientSocketHandle(QObject *parent = nullptr);
public slots:
// 读取客户端数据
void onReadyRead();
//断开
void onDisconnected();
void setSocket(qintptr socketDescriptor);
signals:
// 通知主线程客户端断开连接
void clientDisconnected(qintptr socketDescriptor);
// 通知线程退出
void finished();
private:
QTcpSocket* clientSocket;
qintptr m_socket_fd;
QMutex m_mutex; // 保护Socket读写的互斥锁
};
// .cpp
clientSocketHandle::clientSocketHandle(QObject *parent)
:QObject(parent)
{
qDebug() << "创建客户端处理器,Socket描述符:" << m_socket_fd
<< ",当前是主线程,线程ID:" << QThread::currentThreadId();
}
//收
void clientSocketHandle::onReadyRead()
{
QMutexLocker locker(&m_mutex); // 加锁保证读操作线程安全
QString msg = clientSocket->readAll();
qDebug() << "收到客户端" << m_socket_fd << "消息:" << msg;
// 发,告诉客户端收到了
// 示例:回复客户端
QString reply = "服务端已收到:" + msg;
clientSocket->write(reply.toUtf8() + "\n"); // 加换行符方便客户端解析
clientSocket->flush();
qDebug() << "向客户端" << m_socket_fd << "发送:" << msg;
}
void clientSocketHandle::onDisconnected()
{
qDebug() << "客户端" << m_socket_fd << "断开连接";
clientSocket->close();
//通知主线程
emit clientDisconnected(m_socket_fd);
//通知线程退出
emit finished();
}
// QTcpSocket *socket,
void clientSocketHandle::setSocket(qintptr socketDescriptor)
{
// clientSocket = socket;
m_socket_fd = socketDescriptor;
clientSocket= new QTcpSocket();
bool bindOk = clientSocket->setSocketDescriptor(m_socket_fd);
if (!bindOk) {
qCritical() << "绑定Socket描述符失败!错误码:" << clientSocket->error()
<< ",错误信息:" << clientSocket->errorString()
<< ",描述符:" << m_socket_fd;
} else {
qDebug() << "客户端连接成功,Socket描述符:" << m_socket_fd
<< ",处理线程为子线程,ID:" << QThread::currentThreadId();
}
QString ip = clientSocket->peerAddress().toString();
quint16 port = clientSocket->peerPort();
qDebug() << "客户端 ip 地址:" << ip;
qDebug() << "客户端端口:"<< QString::number(port);
// ========== 绑定信号槽(子线程内执行) ==========
// 接收数据信号
connect(clientSocket, &QTcpSocket::readyRead,
this, &clientSocketHandle::onReadyRead,
Qt::DirectConnection); // 子线程内直接触发
// 客户端断开信号
connect(clientSocket, &QTcpSocket::disconnected,
this, &clientSocketHandle::onDisconnected,
Qt::DirectConnection);
// Socket 错误信号
// connect(clientSocket, &QTcpSocket::errorOccurred,
// this, [this](QTcpSocket::SocketError error) {
// qCritical() << "Socket 错误(描述符:" << m_socket_fd << "):"
// << clientSocket->errorString();
// }, Qt::DirectConnection);
// 打印子线程信息和客户端信息(此时一定有效)
qDebug() << "子线程ID:" << QThread::currentThreadId()
<< "客户端IP:" << clientSocket->peerAddress().toString()
<< "客户端Port:" << clientSocket->peerPort()
<< "描述符:" << m_socket_fd;
}
// tcpServer.h
class TcpServer : public QTcpServer
{
Q_OBJECT
public:
explicit TcpServer(quint16 port = 8888, QObject *parent = nullptr);
signals:
// 信号:向子线程处理器传递已绑定的 QTcpSocket
void passSocketToHandler(qintptr socketDescriptor);
public slots:
void onClientDisconnected(qintptr socketDescriptor);
private:
quint16 m_port;
QTcpServer *m_tcpServer = nullptr;
QMap<qintptr, clientSocketHandle*> m_clientHandlers; // 保存所有在线客户端
QMutex m_clientMutex; // 保护客户端列表的互斥锁
protected:
// 关键:声明重写 incomingConnection 虚函数
//重写QTcpServer的虚函数incomingConnection 用来处理连接产生的socket传给子线程
void incomingConnection(qintptr socketDescriptor) override;
};
TcpServer::TcpServer(quint16 port, QObject *parent)
: QTcpServer(parent),m_port(port)
{
// 关键:加 listen 结果判断
bool listenOk = this->listen(QHostAddress::Any, m_port);
if (listenOk) {
qDebug() << "服务端启动成功,监听端口:" << m_port
<< ",主线程ID:" << QThread::currentThreadId();
} else {
// 打印失败原因,快速定位问题
qDebug() << "服务端启动失败!端口:" << m_port
<< ",原因:" << this->errorString();
}
}
// 清理断开连接的客户端
void TcpServer::onClientDisconnected(qintptr socketDescriptor)
{
QMutexLocker locker(&m_clientMutex);
m_clientHandlers.remove(socketDescriptor);
qDebug() << "清理客户端" << socketDescriptor << ",当前在线数:" << m_clientHandlers.size();
}
void TcpServer::incomingConnection(qintptr socketDescriptor)
{
// ========== 关键1:主线程内创建 QTcpSocket 并绑定描述符 ==========
QTcpSocket* clientSocket = new QTcpSocket(this);
// 绑定描述符(主线程操作,Windows 下 100% 成功)
if (!clientSocket->setSocketDescriptor(socketDescriptor)) {
qCritical() << "主线程绑定描述符失败:" << clientSocket->errorString()
<< ",描述符:" << socketDescriptor;
clientSocket->deleteLater();
return;
}
// 主线程中先获取客户端 IP/端口(此时一定有效)
qDebug() << "新客户端连接:"
<< "IP=" << clientSocket->peerAddress().toString()
<< "Port=" << clientSocket->peerPort()
<< "描述符=" << socketDescriptor;
// ========== 关键2:创建子线程和处理器 ==========
clientSocketHandle* handler = new clientSocketHandle();
QThread* subThread = new QThread(this);
// ========== 关键3:传递 Socket 给子线程处理器 ==========
// 1. 解除 Socket 的父对象(否则无法移到子线程)
clientSocket->setParent(nullptr);
/*
// 2. 连接信号:传递 Socket 和描述符给处理器
connect(this, &TcpServer::passSocketToHandler,
handler, &clientSocketHandle::setSocket,
Qt::QueuedConnection); // 队列连接,适配子线程
//**QueuedConnection 信号的执行,必须等待【当前函数完全跑完】+【回到事件循环】才会执行!见例子A代码
// 3. 发送信号(此时 handler 还未移到子线程,信号会排队到子线程执行)
emit passSocketToHandler(socketDescriptor);
*/
// ========== 关键4:线程生命周期管理 ==========
// 处理器移到子线程
handler->moveToThread(subThread);
// 2. 和 3. 也许可以改成
QMetaObject::invokeMethod(
handler,
"setSocket",
Qt::QueuedConnection,
Q_ARG(qintptr, socketDescriptor)
);
// 客户端断开连接,清理资源
connect(handler, &clientSocketHandle::clientDisconnected,this, &TcpServer::onClientDisconnected);
// 处理器完成后,退出线程
connect(handler, &clientSocketHandle::finished, subThread, &QThread::quit);
// 线程退出时销毁处理器和线程
connect(subThread, &QThread::finished, handler, &clientSocketHandle::deleteLater);
connect(subThread, &QThread::finished, subThread, &QThread::deleteLater);
{
QMutexLocker locker(&m_clientMutex);
m_clientHandlers[socketDescriptor] = handler;
}
// 线程启动
subThread->start();
// 销毁clientSocket
clientSocket->deleteLater();
}
int main()
{
// tcp多线程
// ========== 关键:注册 qintptr 类型 ==========
qRegisterMetaType<qintptr>("qintptr");
TcpServer* server = new TcpServer(8888,this);
}
// 例子A
// MIANWINDOW.H
class Test : public QObject
{
Q_OBJECT
public slots:
void testSlot() {
qDebug() << "【槽函数执行】,线程ID:" << QThread::currentThreadId();
}
};
// 给信号加个发射函数
class Emitter : public QObject
{
Q_OBJECT
signals:
void mySignal(); // 自定义信号,无参数
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
qDebug() << "主线程ID:" << QThread::currentThreadId();
Test testObj; // 接收者
Emitter emitter; // 发送者
// ✅ 关键:QueuedConnection 队列连接
QObject::connect(
&emitter, &Emitter::mySignal,
&testObj, &Test::testSlot,
Qt::QueuedConnection
);
// ==============================================
qDebug() << "1. 开始发射信号";
emit emitter.mySignal(); // 发射队列信号
// 🔥 重点:槽函数 【绝对不会立刻执行】
qDebug() << "2. 信号发射完毕 → 槽函数还没跑!";
qDebug() << "3. 当前函数继续执行,没有被打断";
// 只有进入事件循环,槽才会执行
qDebug() << "4. 调用 processEvents() → 槽函数才执行";
//a.processEvents();
// ==============================================
qDebug() << "5. 函数执行完毕";
return a.exec();
}
输出结果:
主线程ID: 0x5eb4
1. 开始发射信号
2. 信号发射完毕 → 槽函数还没跑!
3. 当前函数继续执行,没有被打断
4. 调用 processEvents() → 槽函数才执行
5. 函数执行完毕
【槽函数执行】,线程ID: 0x5eb4
// 如果没注释a.processEvents();
主线程ID: 0xdd8
1. 开始发射信号
2. 信号发射完毕 → 槽函数还没跑!
3. 当前函数继续执行,没有被打断
4. 调用 processEvents() → 槽函数才执行
【槽函数执行】,线程ID: 0xdd8
5. 函数执行完毕