6

Trying to handle a connected client socket in a new thread from global thread pool:

m_threadPool = QThreadPool::globalInstance();

void TCPListenerThread::onNewConnection()
{
    QTcpSocket *clientSocket = m_tcpServer->nextPendingConnection();
    clientSocket->localPort();
    m_connectThread = new TCPConnectThread(clientSocket);
    m_threadPool->start(m_connectThread);
}

Here's TCPConnectThread:

class TCPConnectThread : public QRunnable {
    TCPConnectThread::TCPConnectThread(QTcpSocket *_socket)
    {
        m_socket = _socket;
        this->setAutoDelete(false);
    }


    void TCPConnectThread::run()
    {
        if (! m_socket->waitForConnected(-1) )
            qDebug("Failed to connect to client");
        else
            qDebug("Connected to %s:%d %s:%d", m_socket->localAddress(), m_socket->localPort(), m_socket->peerAddress(), m_socket->peerPort());

        if (! m_socket->waitForReadyRead(-1))
            qDebug("Failed to receive message from client") ;
        else
            qDebug("Read from client: %s",   QString(m_socket->readAll()).toStdString().c_str());

        if (! m_socket->waitForDisconnected(-1))
            qDebug("Failed to receive disconnect message from client");
        else
            qDebug("Disconnected from client");
    }
}

I have been getting endless errors with these. It seems cross-thread QTcpSocket handling is not feasible(See Michael's answer).

Some errors:

QSocketNotifier: socket notifiers cannot be disabled from another thread  

ASSERT failure in QCoreApplication::sendEvent: "Cannot send events t objects owned by a different thread.  

Should I handle QTcpSocket in a different thread ?
What should I do if I want to handle QTcpSocket in a different thread ?
Or is there way to create a QTcpSocket from a file descriptor ?

Community
  • 1
  • 1
CDT
  • 10,165
  • 18
  • 66
  • 97

1 Answers1

8

I think this page holds your answer:

If you want to handle an incoming connection as a new QTcpSocket object in another thread you have to pass the socketDescriptor to the other thread and create the QTcpSocket object there and use its setSocketDescriptor() method.

To do this, you'll have to inherit from QTcpServer and override the virtual method incomingConnection.

Within that method, create the child thread which will create a new QTcpSocket for the child socket.

For example:

class MyTcpServer : public QTcpServer
{
protected:
    virtual void incomingConnection(int socketDescriptor)
    {
         TCPConnectThread* clientThread = new TCPConnectThread(socketDescriptor);
         // add some more code to keep track of running clientThread instances...
         m_threadPool->start(clientThread);
    }
};

class TCPConnectThread : public QRunnable
{
private:    
    int m_socketDescriptor;
    QScopedPointer<QTcpSocket> m_socket;

public:
    TCPConnectionThread(int socketDescriptor)
        : m_socketDescriptor(socketDescriptor)
    {
        setAutoDelete(false);
    }

protected:    
    void TCPConnectThread::run()
    {
        m_socket.reset(new QTcpSocket());
        m_socket->setSocketDescriptor(m_socketDescriptor);

        // use m_socket
    }
};

or try to use moveToThread() on the socket.

huysentruitw
  • 27,376
  • 9
  • 90
  • 133
  • moveToThread() seems only work with QThread but I'm using QRunnable. – CDT Jun 03 '13 at 12:22
  • 1
    then try to pass the `socketDescriptor` to the `TCPConnectThread` constructor and create a new `QTcpSocket` in the threads `run` method. – huysentruitw Jun 03 '13 at 12:24
  • Really thanks ! But I found that sometimes I would fail to read data from the new QTcpSocket, about 1 in 10 times. Do you see why ? – CDT Jun 03 '13 at 12:37
  • Here's the output of my program: Read from client: Data Disconnected from client Connected to client Read from client: Data Disconnected from client Connected to client Read from client: Data Disconnected from client Connected to client Read from client: [failed] Disconnected from client – CDT Jun 03 '13 at 12:39
  • do you mean `m_socket->readAll()` actually returns the string `[failed]`? If so, then that is what it has received. Anyway, you should call `waitForReadyRead` and `readAll` in a loop until you have all the data you need. Otherwise it is very likely that `waitForReadyRead` will return when only a part of the expected data is in the read buffer. It will not read your mind to determine when incoming data is complete ;) – huysentruitw Jun 03 '13 at 12:45
  • No, it doesn't return a thing. I replaced with `[failed]`... What should I do if I want to read all the data with one `waitForReadyRead` ? Is this possible ? – CDT Jun 03 '13 at 12:49
  • Block the thread for 30 seconds first? Seriously, this would be a very bad design. So: use a loop and add some code that decides when to disconnect the client. – huysentruitw Jun 03 '13 at 12:56
  • @WouterHuysentruit Yup, looks great now! – Nicolas Holthaus Jan 15 '15 at 12:20