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.
Related
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?
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:
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.
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?
I'm trying to move a connection handler (containing a QTCPSocket object) to a separate thread from the QMainWindow object.
So far it works, but if I close the QMainWindow, It crashes and I receive the following:
ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread. Current thread 1680d0. Receiver '' (of type 'QAbstractSocket') was created in thread 34fe0c"
Here is my code:
in the QMainWindow constructor (subclassed):
QThread m_commsThread;
m_pICommsHandler = new CommsHandlerIP();
m_pICommsHandler->moveToThread(&m_commsThread);
// when it starts, we start the connection
connect(&m_commsThread,SIGNAL(started()),m_pICommsHandler,SLOT(connectToHost()));
m_commsThread.start();
in the QMainWindow destructor:
m_commsThread.quit();
m_commsThread.wait();
delete m_pICommsHandler;
About CommsHandlerIP.cpp
CommsHandlerIP::CommsHandlerIP() {
m_TCPSocket = new QTcpSocket(this);
}
CommsHandlerIP::~CommsHandlerIP(void)
{
m_TCPSocket->deleteLater(); // It's going to crash a bit later if I have this line
}
m_TCPSocket should be in the right thread to be deleted but I feel like by closing the main thread, the affinity has become messy.
What should I do?
I think there might be two things happening:
As far as i remember, there were always some issues with Qt4 when calling moveToThread() on a QAbstractSocket.
When you call deleteLater() on your socket, the thread in which it lives is already stopped.
A possible solution could be to subclass your CommsHandlerIP from QThread and override run():
CommsHandlerIP::CommsHandlerIP() :
QThread(),
m_TCPSocket(0)
{
moveToThread(this);
}
void CommsHandlerIP::run()
{
m_TCPSocket = new QTcpSocket(this);
connectToHost();
exec();
m_TCPSocket->close();
delete m_TCPSocket;
}