QT: socket notifiers cannot be enabled from another thread - c++

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.

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?

Related

Reading and writing QTcpSockets through different threads throws QSocketNotifier warning

Basically I want to achive a simple and basic design. I have a client socket which sends and reads settings to a socket server. To avoid blocking in the main thread, the whole socket handling should be done in a diffrent thread. So I created the class MySocket:
MySocket.h (only important parts)
class MySocket : public QThread {
Q_OBJECT
public:
int port;
QHostAddress address;
bool running;
MySocket(QHostAddress addr, int port);
public slots:
void sendMessage(QByteArray data);
protected:
QTcpSocket* socket;
virtual void run();
signals:
void onDataReady(const QByteArray &data);
private slots:
void onReadyRead();
void newConnection();
void disconnected();
};
MySocket.cpp (only important parts)
#include "MySocket.h"
MySocket::MySocket(QHostAddress addr, int port) : port(port), address(addr), running(true)
{
}
void MySocket::onReadyRead()
{
QByteArray datas = socket->readAll();
//Send data to mainThread or somewhere else
emit onDataReady(datas);
}
void MySocket::run()
{
socket = new QTcpSocket();
QObject::connect(socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()));
QObject::connect(socket,SIGNAL(connected()),this,SLOT(newConnection()));
QObject::connect(socket,SIGNAL(disconnected()),this,SLOT(disconnected()));
socket->connectToHost(address, port);
while(running)
{
QApplication::processEvents();
QThread::msleep(10);
}
}
void MySocket::sendMessage(QByteArray data){
qDebug()<< "writing Data ...";
if(socket->state() == QAbstractSocket::ConnectedState)
{
std::string message(data.constData(), data.length());
qDebug() << "SendingData: " << QString::fromStdString(message);
socket->write(data);
}
else{
qDebug() << "Socket not ready for writing";
}
}
The main thread (mainwindow.cpp) just creates and starts the new thread.
mySocket = new MySocket(QHostAddress("127.0.0.1"), 3333);
controlSocket->start();
It also connects the slot and signal for writing.
connect(this, SIGNAL(write(QByteArray)), mySocket, SLOT(sendMessage(QByteArray)));
Whenever I emit the signal using emit(write("some message")); I receive the following error/notifiaciton:
QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread
So first of all, how can I solve this problem? I have created a new thread and I am also using slots/signals as mentioned in many other topics facing similar problems. Is the socket still sending those messages anyway?
The object of class MySocket is a QThread, but the instance of this object itself lives in the main thread.
Only objects created in the run() function live in the other thread. So you should avoid accessing "socket" from the main thread (i.e. from the MySocket class).
2 solutions:
The easiest is to not use threads.
With threads, my advice is to create a wrapper class which encapsulate the socket (which would look very much like MySocket), with signals for dataReceived(QByteArray) and slots for sendData(QByteArray). Then, you create another class extending QThread, also with the same signal and slot and in the run() you create the wrapping class locally and connect its signals and slots to those of the class extending QThread, and then run an event loop forever (QEventLoop::exec()). Or, you can use a standard QThread class (no need to extend) and the method moveToThread() on your wrapper class. This avoids the need to create a class extending QThread.

QT QSocketNotifier: Socket notifiers cannot be enabled or disabled from another thread [duplicate]

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.
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?

How to properly create QUdpSocket on non-gui thread ? Readyread not emitted

I'm writing a network library that wraps the QUdpSocket:
QAbstractSocket *UdpNetworkStreamer::addConnection()
{
QUdpSocket *udpSocket = new QUdpSocket(this);
udpSocket->bind(connection.port, QUdpSocket::ShareAddress);
bool ret = udpSocket->joinMulticastGroup(QHostAddress(connection.ip));
connect(udpSocket, SIGNAL(readyRead()), this, SLOT(readyRead()), Qt::QueuedConnection);
return udpSocket;
}
create a new QUdpSocket.
connect to its readyRead signal.
call readDatagram when readyRead is raised.
All is working fine when I use the library from a Qt GUI application.
The problem starts when another user includes the library used outside of a Qt GUI application.
He calls the addConnection (which creates the socket and calls connect on the readyRead)
The thread on which the addConnection is called is non-Qt.
The addConnection seems to end successfully but the readyRead is never emitted.
Calling read (even though no readyRead was emitted) leads to a successful datagram read.
Fixes that did not work :
moving the the UDP socket thread to the this->thread
QUdpSocket *udpSocket = new QUdpSocket();
udpSocket->moveToThread(this->thread());
udpSocket->setParent(this);
I tried to simulate the problem by calling:void
MainWindow::on__btnOpenMulticastReceiver_clicked()
{
QFuture<void> future = QtConcurrent::run(this,
&MainWindow::CreateMulticastConnection, testHandle);
}
This also led to same symptoms as the one the user had with my library, meaning the readyRead wasn't emitted.
QSignalSpy - I've activated a spy on the readyRead signal; the counter kept on being zero although I could read data directly from the socket. The spy gave valid results (i.e. progressed) when used the socket was initialized on the main thread.
My Questions:
What am I missing and doing wrong ?
What is the simplest way of having the readyRead emitted even though it is not created on the main GUI thread - I couldn't find any sample that works with no GUI or outside Qt threads.
I ended up solving the problem this way :
void MainWindow::OpenConnection()
{
QThread *t = new QThread();
t->start();
SocketWrapper *w= new SocketWrapper();
w->moveToThread(t);
w->metaObject()->invokeMethod(w, "CreateSocket", Qt::QueuedConnection);
}
You must call invokeMethod() with the thread the socket wrapper was movedTo() upon creation of the socket, so that the thread that creates the socket will have a running event loop.
In addition to that, the CreateSocket() needs to be a slot in the SocketWrapper, something like that :
class SocketWrapper : public QObject
{
Q_OBJECT
public:
explicit SocketWrapper(QObject *parent = 0);
signals:
public slots:
void readyRead();
void CreateSocket();
private:
QUdpSocket *_socket;
};
My guess is that you need to add Q_OBJECT macro to the beginning of the class from where you need to emit the signal. Unless you do this, the signal-slot mechanism will not function properly.

how to use QTcpSocket in multithread?

in last question,i don't express my use-case clearly. now i describe my requirement.
in my client,i need to connect to Server with QTcpSocket.in most cases, i can write and read in gui thread(main thread).
how ever,in some time-consuming tasks,i need a single worker thread to execute task. when i connect the server in gui thread,i keep the connection all the time.
if i use a new QTcpSocket in worker thread,must i disconnect the socket in gui thread? and connect to Server again?
class Widget:public QWidget
{
Q_OBJECT
public:
Widget()
{
socket = new QTcpSocket(this);
}
private:
QHostAddress host;
unsigned int port;
QTcpSocket *socket;
private slots:
void startConnect()
{
socket->connectToHost(host,port);
}
void sendData()
{
//just send some bytes
//socket->write();
}
void doTimeConsumingTask()
{
//must i disconnect gui->socket connection before the Worker is running?
// socket->disconnectFromHost();
Worker * w = new Worker();
w->start();
}
}
class Worker:public QThread
{
Q_OBJECT
public:
Worker()
{
}
protected:
void run()
{
QTcpSocket socket;
// must i connectToHost again here ?
// do something consuming time
while(true){
//
QThread::msleep(10);
}
}
private:
QHostAddress host;
unsigned int port;
}
The easy solution to your problem is to not directly access the QTcpSocket from the worker thread. Ultimately, all you probably want to do with the socket from the worker thread is send and/or receive data to/from it. In that case, you can simply have the worker thread emit a signal with a QByteArray parameter, then connect that signal to a slot on the GUI thread that then calls write(const QByteArray& byteArray) on the QTcpSocket. Similarly, you can have your data receive function (whatever is connected to the readyRead() signal of the socket) either live in the worker thread, or live in your main thread, but upon receipt of data that the worker thread needs to process, pass it that data via a signal/slot connection.
The alternative, if you must directly access the socket (say, you need to do more than write to it, maybe you have to be able to close it or whatever), you need to wrap every use of that socket in a mutex, on both threads. This is probably best done by wrapping the raw QTcpSocket in another class that exposes only a few methods that you need, and always has a call like:
QMutexLocker locker(theMutex);
At the beginning of each function.
Is there a reason you can't pass the socket to the thread's constructor?
class Worker : public QThread{
public:
Worker(QTcpSocket* pSocket) : m_pSocket(pSocket) ...
Not sure of your use case but is there a reason you can't do this?
From the docs: http://doc.qt.io/qt-4.8/qiodevice.html
Certain subclasses of QIODevice, such as QTcpSocket and QProcess, are asynchronous. This means that I/O functions such as write() or read() always return immediately, while communication with the device itself may happen when control goes back to the event loop. QIODevice provides functions that allow you to force these operations to be performed immediately, while blocking the calling thread and without entering the event loop. This allows QIODevice subclasses to be used without an event loop, or in a separate thread:

How to know if a client connected to a QTcpServer has closed connection?

I want to pass data from a particular location (Shared Memory) to client applications. A thread continuously polls the SHM for new data and as soon as it gets something, it passes it onto the clients.
There can be multiple instances of such client applications which will be connecting to my (QTcpServer) server.
I am planning to simply create a new QTcpSocket each time my server receives a new connection and store all those sockets in a vector. Later, on each successful poll, I will write the data to all the QTcpSocket's stored in the vector.
But if a client drops a connection (closes his window) I need to know about it! Othewise I will keep writing to QTcpSocket's that are no longer existing and end up crashing.
What is the solution here ?
There are only 2 signals in QTcpServer class:
Signals
void acceptError(QAbstractSocket::SocketError socketError)
void newConnection()
2 signals inherited from QObject
You have a class that contains a vector or list of the sockets. So long as this class is derived from QObject, you can use the signals and slots of QTcpSocket to notify the class when it is disconnected.
So, I'd do something like this: -
class Server : public QObject
{
Q_OBJECT
public:
Server();
public slots:
// Slot to handle disconnected client
void ClientDisconnected();
private slots:
// New client connection
void NewConnection();
private:
QTcpSocket* m_pServerSocket;
QList<QTcpSocket*> m_pClientSocketList;
};
Server::Server()
{ // Qt 5 connect syntax
connect(m_pServerSocket, &QTcpServer::newConnection, this, &Server::NewConnection);
}
void Server::NewConnection()
{
QTcpSocket* pClient = nextPendingConnection();
m_pClientSocketList.push_back(pClient);
// Qt 5 connect syntax
connect(pClient, &QTcpSocket::disconnected, this, &Server::ClientDisconnected);
}
void Server::ClientDisconnected()
{
// client has disconnected, so remove from list
QTcpSocket* pClient = static_cast<QTcpSocket*>(QObject::sender());
m_pClientSocketList.removeOne(pClient);
}
You must set the TCP KeepAplive option to the socket in this way:
mySocket->setSocketOption(QAbstractSocket:: KeepAliveOption, 1);