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 ?
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.
Related
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.
I'm having a problem which was ask here for a similar situation a couple of times, however I couldn't find the solution in those topics.
I'm having a main class where I'd like to extend it with a qt-network support, and this by an additional class. Let me break the source code down for you to the relevant parts:
main class
.h
class MainClass: public QObject{
Q_OBJECT
public:
[...]
private:
NetworkSupport * netSupport;
};
.cpp
MainClass::MainClass()
{
[...]
netSupport
netSupport = new NetworkSupport(this->thread());
netSupport->start();
[...]
}
network class
.h
class NetworkSupport : public QThread
{
Q_OBJECT
public:
NetworkSupport(QThread *mainThread);
~NetworkSupport();
QByteArray readData();
void sendData(QByteArray *msg);
public slots:
void acceptConnection();
void receive();
void run();
private:
QByteArray curMessage;
QThread* libThread;
QEventLoop initWlan;
protected:
QTcpServer server;
QTcpSocket* client;
};
.cpp
NetworkSupport::NetworkSupport(QThread* mainThread)
{
libThread = mainThread;
server.moveToThreaD(libThread);
server.listen(QHostAddress::Any, 5200);
QMetaObject::invokeMethode(&initWlan, "quit", Qt:QueuedConnection);
initWlan.exec();
}
void NetworkSupport::run(){
connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
}
void NetworkSupport::acceptConnection()
{
client = server.nextPendingConnection();
client->moveToThread(libThread);
if (client->state() == QAbstractSocket::ConnectedState)
connect(client, SIGNAL(readyRead()), this, SLOT(receive()));
}
void NetworkSupport::receive()
{
curMessage = client->readAll();
}
void NetworkSupport::sendData(QByteArray* msg)
{
if(client->state() == QAbstractSocket::ConnectedState)
{
client->moveToThread(libThread);
printf("Sending a message %s\n",msg->data());
client->write(*msg);
client->waitForBytesWritten();
}
}
I know I usually don't need to specifiy the moveToThread() this much, but it doesn't change a thing in the end, if all of them are there or removed.
However when executing I get the error message at client->write(*msg) in sendData(), that is:
[...]
Sending a message ?r
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QnativeSocketEngine(0x2afc660), parent's thread is QThread(0x1b59210), current thread is QThread(0x7f6a70026b60)
QSocketNotifier: Can only be used with threads started with QThread
[...]
It does look like a warning, since the the program is still in execution afterwards, but I don't receive any messages from the client (i.e. receive() gets never executed), which is I guess because of the last line from the error message.
Could it be that this error message is just misleading and if so what's it actual meaning, or did I do something completely wrong?
You are doing so many things wrong in this code that I'm not sure what to start with.
NetworkSupport::NetworkSupport(QThread* mainThread)
{
libThread = mainThread;
server.moveToThreaD(libThread);
This will do nothing. server is already in same thread as MainClass instance
server.listen(QHostAddress::Any, 5200);
Server will start to listen on same thread as MainClass
QMetaObject::invokeMethode(&initWlan, "quit", Qt:QueuedConnection);
initWlan.exec();
This mocks me a lot. This will simply start event loop and quit it almost immedietly.
}
void NetworkSupport::run(){
connect(&server, SIGNAL(newConnection()), this, SLOT(acceptConnection()));
}
This will simply run in new thread, call connect and quit thread right after connect statement - your thread will not be running anymore after connect. Also slot acceptConnection will be probably called in same thread as MainClass. I wonder when and where is MainClass created.
Seems for me like you're struggling with to many things at same time. You probably read somwhere, that you should use separate thread for network communicaton, so you won't block other threads. Try single thread approach first. When you get that working, then you should think how to utilize other thread for what you need.
Bonus question: Is this code some kind of plugin for application that may not have Qt eventloop at all? Or is it part of the full-stack Qt application?
There are a few misunderstandings in your code how Qt networking and multi threading works.
First, in Qt docs they say on QTcpServer::nextPendingConnection():
The returned QTcpSocket object cannot be used from another thread. If
you want to use an incoming connection from another thread, you need
to override incomingConnection().
So, you must create your own Server class, that inherits from the QTcpServer, and override incomingConnection() there. In that overriden method you have to post the socket descriptor to other thread where your TCP client will live processing that connection. In this simple example we just emit a signal.
signals:
void myNewConnectionSignal(DescriptorType);
protected:
void incomingConnection(DescriptorType descriptor)
{
emit myNewConnectionSignal(descriptor);
}
Second, what is NetworkSupport thread for? I guess, you want your server object to live and work there? If so then you must rewrite it in other way. Below is the server part only. Note, that QThread already has a QEventLoop and you can use it via exec() in your run().
...
protected:
MyServer *server;
...
NetworkSupport::NetworkSupport()
{
// this is called in the main thread, so "this" lives in main thread
server=0;
client=0;
// say thread to start, but it will be actually started within run()
start();
// sometimes it is recommended to wait here for the thread started
}
NetworkSupport::~NetworkSupport()
{
// this is called in the main thread, say this thread to stop running
quit();
// wait for completion
wait();
}
void NetworkSupport::run()
{
// this is called in server thread
server=new MyServer();
if (server->listen(QHostAddress::Any, 5200))
{
// connect our new signal to slot where we create and init the TCP client, note that Qt::QueuedConnection is used because we want acceptConnection to run in the main thread
connect(server, SIGNAL(myNewConnectionSignal(DescriptorType)), MyClientsPool, SLOT(acceptConnection(DescriptorType)), Qt::QueuedConnection);
// go for infinite event loop
exec();
}
else
{
// do you always ignore errors?
}
// the thread is stopped, let's destroy the server in the thread where it was born
delete server;
server=0;
}
Client is living and working in your main thread. You must not call its methods directly from other threads. NetworkSupport lives in the main thread as well: yes, it incapsulates and manages some other thread but as an QObject itself it lives in the thread we created it in. The methods below are always executed in the main thread because we connected server's signal to NetworkSupport::acceptConnection() using Qt::QueuedConnection which says to Qt that we want the slot to be invoked in the thread where its QObject lives.
private slots:
void socketDisconnected();
...
void NetworkSupport::socketDisconnected()
{
if (client)
{
client->deleteLater();
client=0;
}
}
void NetworkSupport::acceptConnection(DescriptorType descriptor)
{
QTcpSocket* client=new QTcpSocket(this);
client->setSocketDescriptor(descriptor);
connect(client,SIGNAL(disconnected(),this,SLOT(socketDisconnected());
connect(client,SIGNAL(readyRead(),this,SLOT(receive());
}
void NetworkSupport::receive()
{
if (client)
curMessage = client->readAll();
}
void NetworkSupport::sendData(QByteArray* msg)
{
if (client)
{
client->write(*msg);
client->waitForBytesWritten();
}
}
UPDATE
If we just want to hide all network works inside the thread. Note, in the example below there is little error handling and a lot of messages data copies. You might want to optimize it for production.
// private container class incapsulated and working in the thread
class NetworkThreadContainer : public QObject
{
Q_OBJECT
public:
NetworkThreadContainer(QObject* parent=0):QObject(parent),client(0)
{
if (server.listen(QHostAddress::Any, 5200))
{
connect(&server, SIGNAL(newConnection()), this, acceptConnection());
}
else
{
// don't you want to throw exception here?
}
}
public slots:
void sendDataToClient(const QByteArray& barr)
{
if (client)
{
client->write(msg);
client->waitForBytesWritten();
}
}
void acceptConnection()
{
if (!client)
{
client = server.nextPendingConnection();
connect(client, SIGNAL(readyRead()), this, SLOT(receive()));
}
else
{
// what will you do with more than one client connections or reconnections?
}
}
void NetworkSupport::receive()
{
if (client)
{
QByteArray curMessage = client->readAll();
emit messageReceived(curMessage);
}
}
signals:
void messageReceived(const QByteArray&);
public:
QTcpClient* client;
QTcpServer server;
};
// thread class visible to outer program
class NetworkThread : public QThread
{
Q_OBJECT
public:
NetworkThread(QObject* parent=0):QObject(parent)
{
start();
}
~NetworkThread()
{
quit();
wait();
}
bool sendDataToClient(QByteArray* barr)
{
bool ok=false;
// async send data to container's thread
mutex.lock();
if (container)
{
ok=true;
QMetaObject::invokeMethod(
container,
"sendDataToClient",
Qt::QueuedConnection,
Q_ARG(QByteArray, *barr)
);
}
else
{
// container either is not ready yet or already destroyed
}
mutex.unlock();
return ok;
}
signals:
void messageReceived(const QByteArray&);
protected:
void run()
{
mutex.lock();
// I would suggest to catch exception here and assign the error result to some variable for further processing
container=new NetworkThreadContainer();
mutex.unlock();
connect(container,SIGNAL(messageReceived(QByteArray),this,SIGNAL(messageReceived(QByteArray)),Qt::QueuedConnection);
exec();
mutex.lock();
delete container;
mutex.unlock();
}
private:
QMutex mutex;
QPointer<NetworkThreadContainer> container;
};
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);
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 writing an chat client and I want to display messages in MainWindow, like displayTextEdit->append(string); but I'm using the DWORD thread.
What is the way to do it? I have no idea, how to connect this thread with Mainwindow, any ideas?
DWORD WINAPI RECV_MESSAGE(int &s) //&s is a socket, from winsock2
{
gg_header header; // incoming packet type
gg_recv_msg in; // text and user
for(;;)
{
::recv(s,(char*)&header,sizeof(header),0);
if(header.type==RECV_MSG)
{
::recv(s,(char*)&in,sizeof(in),0);
QString string(in.message);
}
}
return 0;
}
thread is created in MainWindow:
CreateThread(NULL,1024,(LPTHREAD_START_ROUTINE) &RECV_MESSAGE,&s,0,NULL));
declare slot in your window that gets QString as param and processes it. Use QMetaObject::invokeMethod like this:
DWORD WINAPI RECV_MESSAGE(int &s) //&s is a socket, from winsock2
{
QObject *ptrToWindowClass;
gg_header header; // incoming packet type
gg_recv_msg in; // text and user
for(;;)
{
::recv(s,(char*)&header,sizeof(header),0);
if(header.type==RECV_MSG)
{
::recv(s,(char*)&in,sizeof(in),0);
QString string(in.message);
QMetaObject::invokeMethod(ptrToWindowClass, "yourDefinedSlot", Qt::QueuedConnection, Q_ARG(QString, string));
}
}
return 0;
}
Such call will process your slot in main thread, so everything will be done safely
Consider creating a QObject derived class that your comm thread can use to communicate with your main window.
class CommSignaller : public QObject
{
Q_OBJECT
signals:
SendMessage(const QString &msg);
public:
Message(const QString &msg)
{
emit SendMessage(msg);
}
}
Pass the main window pointer into the thread function and then connect the CommSignaller SendMessage signal to a slot in your main Window. Using the default connection type of QObject::connect, this communication is inherently thread-safe.