我正在使用 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 派生并重新实现了 voidcomingConnection(qintptr socketDescriptor) ,它工作得很好。我不使用 QThreadPool,它只是一个 QMap,remove_client(Client*) 关闭 QTcpSocket 并停止线程并将其从映射中删除。在 Linux 上一切正常,在 Windows 上我收到以下错误: QSocketNotifier:无法从另一个线程禁用套接字通知程序 QCoreApplication::sendEvent 中的 ASSERT 失败:“无法将事件发送到不同线程拥有的对象....
由这个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* 的列表/映射,但 QThreadPool 可能会在我想要访问它之前为我删除它们。有什么建议吗?
最佳答案
将客户端对象移动到新线程的方式存在问题。实际上,Client::run
与 TcpServer::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/