how to use QTcpSocket in multithread? - c++

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:

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.

Access QTcpSocket from another thread (child thread)

I have created a thread contains a QTcpServer which accepts any incoming connections:
void Service::run() //Service class is a subclass of QThread
{
server->listen(QHostAddress::LocalHost, ServicePortNo);
// server is a private member of Service
while(server->waitForNewConnection(-1)){
QTcpSocket *socket = server->nextPendingConnection();
handle(socket); // This is a pure virtual function
}
}
in handle(QTcpSocket *socket) :
// TimeDateService is subclass of Service
// implementation of pure virtual function handle()
void TimeDateService::handle(QTcpSocket *socket)
{
(new TimeDateSocketHandler(socket))->Start();
}
Note: TimeDateSocketHandler is a subclass of SocketHandler and SocketHandler itself is a subclass of QThread as shown in the following:
void SocketHandler::run()
{
if(!socket->waitForReadyRead(WAIT_TIMEOUT))
{
socket->disconnectFromHost();
socket->close();
return;
}
QByteArray request = socket->readAll();
QByteArray response = Serve(request); // Serve is a pure virtual function
socket->write(response);
socket->waitForBytesWritten(WAIT_TIMEOUT);
socket->disconnectFromHost();
socket->close();
}
And finally here is the TimeDateSocketHandler
QByteArray TimeDateSocketHandler::Serve(QByteArray request)
{
QByteArray response;
response.append(QTime::currentTime().toString().toUtf8());
response.append(QString(SEPARATOR).toUtf8());
response.append(QDate::currentDate().toString().toUtf8());
return response;
}
main function:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
TimeDateService timedateService;
//CalculatorService calculatorService;
//RemoteCMDService remoteCMDService;
timedateService.StartService();
//calculatorService.StartService();
//remoteCMDService.StartService();
return a.exec();
}
In the main function I started the TimeDateService. But when I connect to the server for retrieving time and date, server sends time and date but when the TimeDateSocketHandler wants to close the socket the program crashes:
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to
objects ow ned by a different thread. Current thread 3998779bf0.
Receiver '' (of type 'QNat iveSocketEngine') was created in thread
39985efcd0", file kernel\qcoreapplicatio n.cpp, line 494
Can anyone help me out please how can I fix this problem, Many Thanks
Your problem is this line:
(new TimeDateSocketHandler(socket))->Start();
The parent "socket" lives in the TimeDateService thread, but the child will be in the TimeDateocketHandler thread. Parent and children should be in the same thread when using the Qt event loop.
Here is the relevant part of the documentation:
Event filters are supported in all threads, with the restriction that the monitoring object must live in the same thread as the monitored object. Similarly, QCoreApplication::sendEvent() (unlike postEvent()) can only be used to dispatch events to objects living in the thread from which the function is called. Here is an example of that:
The solution is relatively straight-forward:
You could call the method directly with the invokeMethod method of QMetaObject. You would need to use queued connection to get the slot triggered in the separate thread.
QMetaObject::invokeMethod(new TimeDateSocketHandler(socket),
SLOT(Start()),
Qt::QueuedConnection);
or
Use signal and slots. This means emit a signal instead of direct invokation and then connect the other thread's corresponding slot.
TimeDateSocketHandler *timeDateSocketHandler = new TimeDateSocketHandler(socket);
connect(this, SIGNAL(socketHandled()), timeDateSocketHandler, SLOT(Start()));
emit socketHandled();
or
Use a smart pointer (like QSharedPointer) instead of raw pointer
Move to the socket handling into the other thread.

QT: socket notifiers cannot be enabled from another thread

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?