11

I have a QTcpSocket and I need to control it - write + read using multiple threads.

This works fine in QT4 but in QT5 I am getting this error and it seems that only 1 thread has access to socket at a same time. How do I make it possible for a socket to be accessed by multiple threads?

Basically I want to create 1 thread for reading and 1 thread for writing of data, so that I can asynchronously read and process the data while doing something else in rest of application.

Note: answer to Qt - Handle QTcpSocket in a new thread doesn't help here, because it describes how to transfer socket from thread 1 to thread 2 and then use it from thread 2 only. I need to use it from both threads.

Community
  • 1
  • 1
Petr
  • 13,747
  • 20
  • 89
  • 144
  • Why can't you just send messages via signals and slots to the thread hosting the QTcpSocket instance? When you state "I'm getting this error", it would be helpful to add the error to the question. – TheDarkKnight Nov 18 '13 at 15:26
  • 1
    Well, one read and one write thread should be OK. More than one recv is gonna be a problem because a TCP byte stream cannot send messages longer than one byte - any rx thread may get in and rx a partial buffer only. I would say that @Merlin069 has the solution to this - queue request/response objects to one thread. – Martin James Nov 18 '13 at 15:37
  • you could use cross-thread signal/slots to send around the read data/the data to write. I still wonder what the use case is to share a single socket, I'd expect it to require much higher-level synchronization (as Martin suggests) to be of any use. – Frank Osterfeld Nov 18 '13 at 15:43
  • @Merlin069 "this error" is error in topic (socket notifiers cannot be enabled from another thread) – Petr Nov 18 '13 at 16:15
  • @MartinJames yes - that's what I am trying to accomplish - one for writing, and 1 for reading, but apparently I can't create any thread for this – Petr Nov 18 '13 at 16:15
  • QTCPSocket and QSocketNotifier are intended to be used in a non-blocking manner; so multiple threads per socket should not be necessary. Perhaps you can redesign your networking code to only use one thread? That would be much easier to get working correctly. – Jeremy Friesner Nov 18 '13 at 16:33
  • If you're getting an error when more than one thread tries to read or write to the socket at the same time, why not consider putting a mutex in place that locks the thread whenever reading or writing takes place and then unlock when done? – RobbieE Nov 19 '13 at 08:00

1 Answers1

9

You can only directly interact with the socket from one thread (the thread must have an event loop running - so you should have called exec() on it). If you want to read/write from another thread, you will need to use Signals/Slots.

Connecting a Signal emitted on one thread to a Slot of an object on another thread using the default connection type (Qt::AutoConnection) will automatically ensure a safe thread transfer occurs (using a queued connection). You can explicitly connect a Signal to a Slot using Qt::QueuedConection, but Qt::AutoConnection should work fine.

// Lives on thread 1
class MySocketOwner : public QObject
{
    Q_OBJECT

public:
    MySocketOwner(QObject *Parent = 0)
        : QObject(Parent)
    {
        Socket = new QTcpSocket(this);
        connect(Socket, SIGNAL(readyRead()), this, SLOT(Read()));
    }

    ~MySocketOwner()
    {
        delete Socket;
    }

public slots:
    void Read()
    {
        QByteArray Data = Socket->readAll();
        // Do something with the data
    }

    void Write(QBytrArray Data)
    {
        // Must always be called on thread 1
        Socket->write(Data);
    }

private:
    QTcpSocket *Socket;
};

// Lives on thread 2
class MySocketWriter : public QObject
{
    Q_OBJECT

public:
    MySocketWriter(QObject *Parent = 0)
        : QObject(Parent)
    {
        // If this is on another thread, connection will be queued
        connect(this, SIGNAL(Write(QByteArray)), MySocketOwnerPointer, SLOT(Write(QBytrArray Data)));
        QByteArray Data;
        // Fill with data

        // An event gets put onto thread 1's event queue after this
        emit Write(Data);
    }

signals:
    void Write(QByteArray Data);
};

Like the comments on your question say, you need to think carefully about why you need this behaviour, do you really need to read the same data received by the socket on 2 separate threads?

oggmonster
  • 4,672
  • 10
  • 51
  • 73
  • 7
    I don't need to read data on 2 threads. I want to read them in 1 thread and write in another one – Petr Nov 18 '13 at 16:17