gpt4 book ai didi

c++ - Qt QTcpSocket() readReady 信号在多线程服务器应用程序中从不触发(从不调用插槽)。 waitForReadyRead() 方法工作正常

转载 作者:搜寻专家 更新时间:2023-10-31 01:44:12 26 4
gpt4 key购买 nike

我正在使用 QTcpServer 和 QTcpSocket 编写线程化的 TcpServer(每个客户端都在自己的线程中)。客户端应用程序正常工作并每 3 秒发送一次数据,但 readReady() 信号从未触发,这意味着我的 receive_data() 函数从未被调用。当我自己使用 socket->waitForReadyRead() 并调用 receive_data() 时,一切正常。请查看下面的代码,也许我在使用 Qt 提供的 moveToThread/connect 功能时犯了一些错误。

客户端.h

#ifndef CLIENT_H
#define CLIENT_H

#include <QThread>
#include <QTcpSocket>
#include <QHostAddress>
#include "PacketDefinitions.h"
#include "tcpserver.h"

class Client : public QObject
{
Q_OBJECT
public:
explicit Client(int socket,TcpServer *parent,bool auto_disconnect = true);
~Client();
bool isGameServer(){return is_gameserver;}
GameServerPacket getGameServerData(){return gameserver;}
void run();
private:
QTcpSocket* client;
TcpServer *parent_server;
int socket;
GameServerPacket gameserver;
ClientPacket clientdata;
bool is_gameserver;
bool auto_disconnect;
QHostAddress client_ip;
quint16 client_port;
signals:
void disconnected(Client *);
private slots:
void remove_from_clientlist();
void receive_data();
void display_error(QAbstractSocket::SocketError error);
};

#endif // CLIENT_H

客户端.cpp

#include "client.h"
#include "PacketDefinitions.h"
#include "time.h"
#include <iostream>

Client::Client(int _socket, TcpServer *parent,bool _auto_disconnect)
{
auto_disconnect = _auto_disconnect;
parent_server = parent;
is_gameserver = false;
socket = _socket;
}

void Client::run(){
client = new QTcpSocket();

if(client->setSocketDescriptor(socket) == false){
std::cout << client->errorString().toStdString() << std::endl;
remove_from_clientlist();
return;
}

connect(client,SIGNAL(disconnected()),this,SLOT(remove_from_clientlist()));
if(connect(client,SIGNAL(readyRead()),this,SLOT(receive_data()),Qt::DirectConnection) == false) return;
connect(client,SIGNAL(error(QAbstractSocket::SocketError)),this,SLOT(display_error(QAbstractSocket::SocketError)));

client_ip = client->peerAddress();
client_port = client->peerPort();

std::cout << "New incomming connection " << client->peerAddress().toString().toStdString() << ":" << client->peerPort() << std::endl;

//this works fine
// while(client->waitForReadyRead()){
// receive_data();
// }
}

void Client::receive_data(){
QDataStream stream(client);
stream.setVersion(QDataStream::Qt_5_2);
quint32 magic; stream >> magic;
//interpret data
if(magic == GAMESERVER_MAGIC){
is_gameserver = true;
gameserver.Read(stream);
gameserver.port = client_port;
gameserver.ip = client_ip;
time(&(gameserver.last_update));
parent_server->add_server(gameserver.ip.toString(),gameserver);
std::cout << "GameServer " << gameserver.name << " registerd" << std::endl;
}else if(magic == CLIENT_MAGIC){
is_gameserver = false;
clientdata.Read(stream);
//get nearby servers
GameServerListPacket server_list = parent_server->getServerList(clientdata);
QDataStream outstream(client);
server_list.Write(outstream);
std::cout << "Sending ServerList(" << server_list.server_count << ") to " << client->peerAddress().toString().toStdString() << std::endl;
if(auto_disconnect){
//client->flush();
client->waitForBytesWritten();
}

}else{
std::cout << "Unknown package " << magic << std::endl;
}

//not enough data read, somthing is wrong, just for debugging
if(client->bytesAvailable()> 0) std::cout << "BytesAvailable " << client->bytesAvailable() << std::endl;

if(auto_disconnect) remove_from_clientlist();//close the connection once the serverlist was deployed
}

在 TcpServer.cpp 中,当 QTcpServer 发出 newConnection() 时调用 add_client():

void TcpServer::add_client(){
while(server->hasPendingConnections()){
QTcpSocket *socket = 0;
if(thread_pool.size() < max_connections && (socket = server->nextPendingConnection())){
QThread *thread = new QThread();
Client * client = new Client(socket->socketDescriptor(),this,auto_disconnect);
client->moveToThread(thread);
client->run();
thread->start();
connect(client,SIGNAL(disconnected(Client*)),this,SLOT(remove_client(Client*)));
WRITELOCK(thread_pool.insert(client,thread));
}
}
}

调用 client->run() 和 thread->start() 的顺序似乎无关紧要。前段时间代码(不是这个确切的代码)工作正常但我不记得我改变了什么导致它失败。感谢您的帮助!

提前致谢费边

编辑 1:

我从 QTcpServer 派生并重新实现了 void incomingConnection(qintptr socketDescriptor),效果很好。我不使用 QThreadPool,它只是一个 QMap,remove_client(Client*) 关闭 QTcpSocket 并停止线程并将其从映射中删除。在 Linux 上一切正常,在 Windows 上我收到以下错误:QSocketNotifier:无法从另一个线程禁用套接字通知程序 QCoreApplication::sendEvent 中的断言失败:“无法将事件发送到其他线程拥有的对象....

Screenshot

由此引起的remove_client(Client*)

void TcpServer::remove_client(Client *client){
//disconnect(client,SIGNAL(disconnected(Client*)),this,SLOT(remove_client(Client*)));
lock.lockForWrite();
QMap<Client*,QThread*>::iterator itr = thread_pool.find(client);
if(itr != thread_pool.end()){
//delete itr.key(); causes the problem on windows
itr.value()->quit();
itr.value()->wait();
delete itr.value();
thread_pool.erase(itr);
}
lock.unlock();
}

我应该在哪里以及如何释放 Client 对象?如果我使用 QThreadPool,则无法遍历客户端,以防我想向多个客户端发送消息。我可以使用仅包含 Client* 的列表/ map ,但 QThreadPool 可能会在我想访问它之前为我删除它们。有什么建议吗?

最佳答案

将客户端对象移动到新线程的方式存在问题。实际上,Client::runTcpServer::add_client 在同一个线程中执行。此外,QTcpSocket 客户端保留在默认线程中,而其容器(Client 类)移动到新线程。这就是为什么与 Qt::DirectConnection 类型的连接不起作用。试试这个:

class Client : public QObject
{
Q_OBJECT
...
public slots:
void run();
...
}

Client::Client(int _socket, TcpServer *parent,bool _auto_disconnect)
{
...
client = new QTcpSocket(this);
}

void Client::run()
{
...
connect(client, SIGNAL(readyRead()), this, SLOT(receive_data()));
...
}

以下是将客户转移到新线程的方法:

void TcpServer::add_client()
{
...
QThread *thread = new QThread();
Client * client = new Client(socket->socketDescriptor(),this,auto_disconnect);
client->moveToThread(thread);
connect(thread, SIGNAL(started()), client, SLOT(run()));
thread->start();
...
}

关于c++ - Qt QTcpSocket() readReady 信号在多线程服务器应用程序中从不触发(从不调用插槽)。 waitForReadyRead() 方法工作正常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23876634/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com