Walter white
Walter white

Reputation: 21

QTcpSocket: message not send from another thread

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

Answers (2)

wtom
wtom

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

Walter white
Walter white

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

Related Questions