QTcpSocket:消息不从另一个线程发送

时间:2018-07-13 10:55:42

标签: c++ multithreading qt singleton qtcpsocket

我知道这个主题已经有很多话题,但是没人可以纠正我的问题... 我希望有人能找到解决方案。

主题:我正在开发多线程游戏服务器。 然后,我重载了TcpServer来写我的等等……

void TcpServerCustomersHandler::incomingConnection(qintptr socketDescriptor)
{
    ThreadCustomer* client = new ThreadCustomer(socketDescriptor);
    connect(client, &ThreadCustomer::finished, client, &ThreadCustomer::deleteLater);
    client->start();
}

我编写了另一个使QThread重载的类并编写了我的连接。 在“问题/答案”模式下,它起作用。我收到消息并回复。

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);
}

但由于它是一个游戏服务器,我希望他能够发送“通知”。这意味着向玩家发送消息之前不会收到任何消息。 这些通知由单例“ InstanceManager”发送。

void ThreadCustomer::onReadyRead_InstanceManager(QByteArray message)
{
    m_tcpSocket->write(message);
}

这是我的问题。当调用“ write()”时,没有消息发出,并且我有一条著名的消息:“ QSocketNotifier:无法从另一个线程启用或禁用套接字通知程序”。 我知道我收到此消息是因为尽管有连接,但还是从另一个线程调用了“ write()”。

不幸的是,我找不到解决该问题的解决方案。有人有主意吗?

2 个答案:

答案 0 :(得分:0)

好的,感谢@wtom,我想出了第一个基于队列的解决方案。

我在QThread中使用QTimer来处理消息并避免冲突。 对于其他人:

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);
    }
}

我不知道这是否是更好的解决方案,但是可行:)

答案 1 :(得分:0)

我不完全记得它是如何完成的,因此它是更多的伪代码,但关键是当您将 InstanceManager :: readyRead 连接到 ThreadCustomer :: onReadyRead_InstanceManager 时(在就绪读取事件上写入-是否有此意图?), ThreadCustomer :: onReadyRead_InstanceManager 将在创建 ThreadCustomer 的线程中执行,该线程执行 void TcpServerCustomersHandler: :incomingConnection(qintptr socketDescriptor)。这不是您想要的。您必须将 ThreadCustomer thread affinity更改为执行 ThreadCustomer :: run()

的线程

编辑 ThreadCustomer是从QThread派生的,对吧?

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();
}