Reputation: 21
I know there is already many topics at this subject but noone to correct my problem... I hope someone will find the solution.
Subject: I'm developping a multithreading game server. Then, I overload TcpServer to write mine and so on...
void TcpServerCustomersHandler::incomingConnection(qintptr socketDescriptor)
{
ThreadCustomer* client = new ThreadCustomer(socketDescriptor);
connect(client, &ThreadCustomer::finished, client, &ThreadCustomer::deleteLater);
client->start();
}
I have written another class which overload QThread and wrote my connects. In "question/answer" mode, it works. I receive message and I respond.
void ThreadCustomer::run()
{
qDebug() << "Starting thread: " << m_socketDescriptor;
m_tcpSocket = new QTcpSocket();
if(!m_tcpSocket->setSocketDescriptor(m_socketDescriptor))
{
qCritical() << "Error creation thread:" << m_tcpSocket->errorString();
return;
}
connect(m_tcpSocket, &QTcpSocket::readyRead, this, &ThreadCustomer::onReadyRead_TcpSocket, Qt::DirectConnection);
connect(m_tcpSocket, &QTcpSocket::disconnected, this, &ThreadCustomer::onDisconnected_TcpSocket, Qt::DirectConnection);
connect(InstanceManager::instance(), &InstanceManager::readyRead, this, &ThreadCustomer::onReadyRead_InstanceManager);
exec();
}
void ThreadCustomer::onReadyRead_TcpSocket()
{
QByteArray message = m_tcpSocket->readAll();
//works...
m_tcpSocket->write(message);
}
BUT as it is a game server, I want he could be able to send "notification". That means send a message to the player without receive anything before. These notifications are send by a singleton "InstanceManager".
void ThreadCustomer::onReadyRead_InstanceManager(QByteArray message)
{
m_tcpSocket->write(message);
}
And here is my problem. When the "write()" is called, no message is emit and I have the famous message: "QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread". I know I have this message because "write()" is called from another thread despite the connect.
Unfortunately, I don't find the solution to fix that. Does someone has an idea ?
Upvotes: 2
Views: 600
Reputation: 575
I don't remember exactly how it is done, so it is more pseudocode, but the point is when you connect InstanceManager::readyRead to ThreadCustomer::onReadyRead_InstanceManager (write on ready read event - is it intended?), ThreadCustomer::onReadyRead_InstanceManager will be executed in thread that created ThreadCustomer, the one that executes void TcpServerCustomersHandler::incomingConnection(qintptr socketDescriptor). It is not what you want. You have to change thread affinity of ThreadCustomer to thread that executes ThreadCustomer::run()
Edit ThreadCustomer is derived from QThread, right?
void TcpServerCustomersHandler::incomingConnection(qintptr socketDescriptor)
{
ThreadCustomer* client = new ThreadCustomer(socketDescriptor);
client->moveToThread(&client); // in canonical way QThread and ThreadCustomer are different objects: client->moveToThread(&thread);, but may be it will work like this?
connect(client, &ThreadCustomer::finished, client, &ThreadCustomer::deleteLater);
client->start();
}
Upvotes: 0
Reputation: 21
Okay, thank to @wtom, I figure out a first solution based on queue.
I use a QTimer in my QThread to handle message and avoid conflict. For others:
void ThreadCustomer::run()
{
qDebug() << "Starting thread: " << m_socketDescriptor;
m_tcpSocket = new QTcpSocket();
if(!m_tcpSocket->setSocketDescriptor(m_socketDescriptor))
{
qCritical() << "Error creation thread:" << m_tcpSocket->errorString();
return;
}
m_timerWritting = new QTimer();
connect(m_timerWritting, &QTimer::timeout, this, &ThreadClient::onTimeOut_timerWritting, Qt::DirectConnection);
m_timerWritting->start(500);
connect(m_tcpSocket, &QTcpSocket::readyRead, this, &ThreadClient::onReadyRead_TcpSocket, Qt::DirectConnection);
connect(m_tcpSocket, &QTcpSocket::disconnected, this, &ThreadClient::onDisconnected_TcpSocket, Qt::DirectConnection);
connect(InstanceManager::instance(), &InstanceManager::readyRead, this, &ThreadClient::onReadyRead_InstanceManager, Qt::QueuedConnection);
exec();
}
void ThreadCustomer::onReadyRead_InstanceManager(QByteArray message)
{
m_listMessageToSend.append(message);
}
void ThreadCustomer::onTimeOut_timerWritting()
{
if(m_listMessageToSend.count() > 0)
{
QByteArray message = m_listMessageToSend.takeFirst();
m_tcpSocket->write(message);
}
}
I don't know if it is the better solution but that works :)
Upvotes: 0