I'm developing an application that made sense that I wrapped my classes around the implemented QTcpSocket (since it's a TCP protocol, and I need to be able to use the raw socket and the added features as seemlessly as possible).
So, for the sake of simplicity, my code is structured like this:
class MyTCPSocket : public QTcpSocket
{
Q_OBJECT
// ....
}
class MyTCPServer : public QTcpServer
{
Q_OBJECT
// ....
}
And with QTcpServer, the default nextPendingConnection() function returns an instance of QTcpSocket, which I can't cast up to MyTCPSocket (as far as I'm aware, QTcpSocket doesn't have a copy constructure).
What's my best option for handing this? I was thinking that I could create a new MyTCPSocket and simply set the socket descriptor to the one returned by nextPendingConnection() from MyTCPServer, but I wasn't sure how that would work when the old socket gets deleted.
Is there a better way to handle this situation?
Exactly that: override incomingConnection but don't call the base class, instead create an instance of your QTcpSocket subclass and use setSocketDescriptor on it. If you want to keep QTcpSocket "signal behaviour", also call addPendingConnection and emit newConnection.
Then just downcast the socket you get from nextPendingConnection to your QTcpSocket subclass.
Related
I am attempting to fake a transfer between two QTcpSockets that happen to be in the same class (which is a googletest fixture). The focus of this is to see if I can send multiple messages between the two and properly extract them again. However, it seems that the two sockets won't connect. Most of the posts I've seen that relate to this don't come up with a working answer, and being as this is definitely not the intended means of use, I'm not sure that there is a simple one.
What I have for setting up the connection:
class TTest : public ::testing::Test, public QObject
{
//Q_OBJECT
protected:
QTcpServer qserv;
QTcpSocket qtcpsock1; //send
QTcpSocket *qtcpsock2; //rcv
TTest() : qserv(this), qtcpsock1(this)
{
while (!qserv.isListening())
{
qserv.listen();
}
qtcpsock1.connectToHost(QHostAddress::LocalHost, qserv.serverPort());
qtcpsock2 = qserv.nextPendingConnection();
qtcpsock1.waitForConnected();
if (!qtcpsock2) std::cout << "socket 2 not initialized\n";
qserv.close();
}
}
Signals/slots currently not in use.
The problem with this is that the event polling loop of the application will not run, so no events will be handled and your function calls will simply not work without it.
In short, the waitForConnected() call will wait for an event that never happens.
The natural solution with Qt is of course to use signals and slots, and let the normal application event loop run.
On a slightly related note: If you want internal communication within the same process consider something other than (heavy and complex) TCP sockets. Simple message queues? Anonymous pipes? Plain strings or arrays?
Disclaimer: I am relatively new to Qt and any type of programming that revolves around Threads and Networking. I have also adopted a lot of code from Qt Examples, API, and other online examples.
All code can be found on GitHub. This code is relatively as simple as it can get minus striping out GUI. I figure supplying it this way would help as well versus just pasting the code below.
I want to use and believe I need to use Threads as I need multiple clients send a request to the server, the server run some SQL code, then spit out the results back to the client (basically deriving a MySQL Server, but specific to what I am doing). Right now though, I am just working on learning the workings of it all.
With all that being said, as the Title states.. My client can connect to the server, the server sets up the thread, and will receive data (a String) through the readReady. After the data is read in, for right now I am just trying to echo it back to the client. It will do this, but only once. Then it spits out:
QObject: Cannot create children for a parent that is in a different thread.
(Parent is QNativeSocketEngine(0x266cca92ea0), parent's thread is serverThread(0x266cca9ed60), current thread is QThread(0x266cac772e0)
I cannot send any further data to the server unless I have the client reconnect, then after the data is sent, it will do its job but then spit out the same error and cease functioning. I have tried quite a bit of different things, but cannot seem to fix the issue. I even tried setting up a SIGNAL/SLOT for this as suggested in API:
It is important to remember that a QThread instance lives in the old thread that instantiated it, not in the new thread that calls run(). This means that all of QThread's queued slots will execute in the old thread. Thus, a developer who wishes to invoke slots in the new thread must use the worker-object approach; new slots should not be implemented directly into a subclassed QThread.
Anyway, any help would be greatly appreciated! My Code is below..
Server
ServerThread.cpp
// Project
#include "ServerDialog.h"
#include "ServerThread.h"
ServerThread::ServerThread(qintptr _socketDiscriptor, QObject *parent /*= 0*/)
: QThread(parent)
{
socketDiscriptor = _socketDiscriptor;
}
void ServerThread::run()
{
emit threadStarted(socketDiscriptor);
// Start Thread
clientSocket = new QTcpSocket;
// Set SocketDisc
if (!clientSocket->setSocketDescriptor(socketDiscriptor))
{
emit error(clientSocket->error());
return;
}
// Connect Socket and Signal
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(clientSocket, SIGNAL(disconnected()), this, SLOT(disconnected()));
//// Loop Thread to Stay Alive for Signals and Slots
exec();
}
void ServerThread::readyRead()
{
QDataStream in(clientSocket);
in.setVersion(QDataStream::Qt_5_7);
in.startTransaction();
QString dataReceived;
in >> dataReceived;
if (!in.commitTransaction())
{
emit readyReadError(socketDiscriptor);
return;
}
emit readyReadMessage(socketDiscriptor, dataReceived);
echoData(dataReceived);
}
void ServerThread::disconnected()
{
emit threadStopped(socketDiscriptor);
clientSocket->disconnect();
clientSocket->deleteLater();
this->exit(0);
}
void ServerThread::echoData(QString &data)
{
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_7);
out << data;
clientSocket->write(block);
}
So in ServerThread.cpp when echoData is called, that is when the error shows up and the Socket ceases functioning.
Any and all help will be appreciated. I know there are a few other posts regarding "Cannot create children for..." in regards to Threads. But I did not find any of them helpful. The one thing that I did find interesting but did not understand was maybe using moveToThread() but a lot of mixed comments on that.
I learn best through code examples along with explanation versus just an explanation or pointer to API. Thank you!
Most of Qt network functions are asynchronous; they do not block the calling thread. There is no need to mess up with threads if you are using QTcpSockets. In fact, creating a thread for every socket is an overkill, since that thread will spend most of its time just waiting for some network operation to finish. Here is how I would implement a single-threaded echo server in Qt:
#include <QtNetwork>
#include <QtCore>
//separate class for the protocol's implementation
class EchoSocket : public QTcpSocket{
Q_OBJECT
public:
explicit EchoSocket(QObject* parent=nullptr):QTcpSocket(parent){
connect(this, &EchoSocket::readyRead, this, &EchoSocket::EchoBack);
connect(this, &EchoSocket::disconnected, this, &EchoSocket::deleteLater);
}
~EchoSocket() = default;
Q_SLOT void EchoBack(){
QByteArray receivedByteArray= readAll();
write(receivedByteArray);
disconnectFromHost();
}
};
class EchoServer : public QTcpServer{
public:
explicit EchoServer(QObject* parent= nullptr):QTcpServer(parent){}
~EchoServer() = default;
//override incomingConnection() and nextPendingConnection()
//to make them deal with EchoSockets instead of QTcpSockets
void incomingConnection(qintptr socketDescriptor){
EchoSocket* socket= new EchoSocket(this);
socket->setSocketDescriptor(socketDescriptor);
addPendingConnection(qobject_cast<QTcpSocket*>(socket));
}
EchoSocket* nextPendingConnection(){
QTcpSocket* ts= QTcpServer::nextPendingConnection();
return qobject_cast<EchoSocket*>(ts);
}
};
int main(int argc, char* argv[]){
QCoreApplication a(argc, argv);
EchoServer echoServer;
echoServer.listen(QHostAddress::Any, 9999);
QObject::connect(&echoServer, &EchoServer::newConnection, [&](){
EchoSocket* socket= echoServer.nextPendingConnection();
qDebug() << "Got new connection from: " << socket->peerAddress().toString();
});
return a.exec();
}
#include "main.moc"
Notes:
This server has the ability to handle more than one client at the same time, since there is no blocking. The thread will just respond to the event that happens with the appropriate action; So, if that event was a new connection, it will create a new EchoSocket object to handle it and prints a statement out to qDebug(), and if that event was receiving something on a previously created socket, the same thread will echo received data back and close the connection. It will never block on a single connection waiting for data to arrive nor it will block waiting for a new connection to arrive.
Since you mention using some SQL queries in response for some connections later in your project. Please avoid threading since an SQL database connection in Qt can be used only from the thread that created it, see docs here. So, You'll have to either create a new database connection for each thread (and thus for each connection) in your application (and this is beyond just overkill), or switch later to a single threaded design.
In this section, I am explaining why threading does not work for you the way you are doing it:
You should not be declaring slots in your QThread subclass, Instead, use worker QObjects and move them to QThreads as needed.
The quote you have provided in your question is the exact explanation for why you get this warning. The ServerThread instance you create will be living in the main thread (or whatever thread that created it). Now let's consider this line from your code:
connect(clientSocket, SIGNAL(readyRead()), this, SLOT(readyRead()));
The signal readyRead() will be emitted from the current ServerThread instance (since the clientSocket object that emits it lives there), However, the receiver object is the current ServerThread instance, But that lives in the main thread. Here is what the documentation says:
If the receiver lives in the thread that emits the signal, Qt::DirectConnection is used. Otherwise, Qt::QueuedConnection is used.
Now, the main point of Qt::QueuedConnection is executing the slot in the receiver object's thread. This means that, your slots ServerThread::readyRead() and ServerThread::disconnected will get executed in the main thread. This is most likely not what you meant to do, since you'll end up accessing clientSocket from the main thread. After that, any call on clientSocket that results in child QObjects being created will result in the warning you get (you can see that QTcpSocket::write() does this here).
Mixed comments of movetothread are linked mostly to usage of it to move thread object to itself.
The quote hints that the members of QThread aren't designed to be called from worker. Strictly proper way to call signal would be by using worker object model, that was shown in Qt examples and explained a few times on QT-related blogs:
class Worker : public QObject
{
Q_OBJECT
private slots:
void onTimeout()
{
qDebug()<<"Worker::onTimeout get called from?: "<<QThread::currentThreadId();
}
};
class Thread : public QThread
{
Q_OBJECT
private:
void run()
{
qDebug()<<"From work thread: "<<currentThreadId();
QTimer timer;
Worker worker;
connect(&timer, SIGNAL(timeout()), &worker, SLOT(onTimeout()));
timer.start(1000);
exec();
}
};
worker constructed inside run() is "property" of the thread it created, so figuratively speaking, it is slaved to its context. The same effect maybe achieved if you create worker in other thread, then move it to this thread before connection was made. When you connect signal to slot of the QThread itself, you connect child thread to thread it was created by.
Use of
connect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()), Qt::DirectConnection);
or creating connection from your thread sometimes seems to achieve proper result, but not in this case, where you try use objects constructed in different threads together. Calling moveToThread(this) in constructor is a thing not recommended to do.
I try to use QTcpSserver, which would keep connection with one and only one client at a time, until the client disconnects. So, I keep the client with a member pointer in my class.
The problem arises here: In the examples I see on the internet, after disconnected(), it is called deleteLater(). Good, but I would use this class-member pointer again for another connection. Remember that the server keeps one and only one client at a time. So, what if the socket object is deleted after another connection assigned on it?
What I mean is:
class TcpServer(QObject* o) : public QTcpServer {
...
private:
QTcpSocket* client;
}
void TcpServer::connected() {
client = this->nextPendingConnection();
this->pauseAccepting();
connect(client, SIGNAL(disconnected()), client, SLOT(clientDisconnected()));
}
void TcpServer::clientDisconnected() {
client->deleteLater();
this->resumeAccepting();
}
Scenario is this:
Client connected. So, client = nextPendingConnection();
Server paused listening. Does not accept new connection.
Client is disconnected. client needs to be released. So, client->deleteLater() is calleed.
Server continues listening.
New connection comes. So, I need to client = nextPendingConnection();
But, previous client object was deleted? Maybe? Maybe not? What if event loop tries to delete client, after I have assigned the new connection to it in step 5?
So, how would I keep one and only one client, while deleting previous disconnected ones?
Would it be safe if I do this?
void TcpServer::clientDisconnected()
{
QSocket* ptr = client;
ptr->deleteLater();
...
}
I will cite Qt documentation about it:
The object will be deleted when control returns to the event loop.
So deleteLater() is a delayed delete. The object is to be regarded as deleted as soon as the call deleteLater() was made.
Your nextPendingConnection() call will create another object that need to be deleted some time later.
However in your case you only allow one pending connection as you said and disallow accepting until client gets disconnected. I this case it should be safe, in other cases you could overwrite your client pointer and will lose control over it (memory leak).
Even in your case, I would prefer this solution:
void TcpServer::clientDisconnected()
{
if (qobject_cast<QAbstractSocket*>(sender())) {
sender()->deleteLater();
}
...
}
This would also be safe if more than one connection is allowed in future changes of your application.
As i understand nextPendingConnection(); will return pointer to new QTcpSocket class object so you have nothing to worry about.
deleteLater() will scheduled for deletion only your old object. QTcpSocket* client contains only pointer to QTcpSocket class object. When you calling deleteLater() Qt will delete only object to which client was pointed at time of calling this function.
I have one c++ class and have one signal in it and want to connect that signal with slot another C++ class. Here is my code
class Data : public QObject
{
Q_OBJECT
public:
static Data *instance(void);
signals:
void sendUserID(const QString& userId);
private:
static Data *s_instance;
};
here is my Slot in another class
void DataDetails::seTUserID(const QString user_id)
{
QAndroidJniObject user_id = QAndroidJniObject::fromString(user_id);
QAndroidJniObject::callStaticMethod<void>("com/user/data/userActivity",
"setUserID",
"(Ljava/lang/String;)V",
user_id.object<jstring>());
}
The idea is to access the value of user_id from Data class to DataDetails class
The connection is trying is
QObject::connect(&s_instance, SIGNAL(sendUserID(uid), this, SIGNAL(setUserID(uid))
any other id to get uid to other class is also fine ..
Generally speaking, when you encounter an issue with QObject::connect...
Make sure you have your declarations in order:
Both classes need the Q_OBJECT macro in their declaration.
Make sure your slot is actually declared as a slot (i.e. is part of a public slot: section).
Because connecting signals and slots just uses character strings evaluated at run-time, it's possible to write absolute nonsense and have it compile. In my experience, errors caused by typos are pretty common.
Always test in an environment where you can see your application's console output. Failed connect calls will usually trigger an error message printed to stderr.
Double-check your method names and signatures. Code will still compile even if you've made a typo!
For debugging, use assertions (e.g. bool c = connect(...); Q_ASSERT(c);) to catch missed connections early.
Alternatively, you can use the QMetaMethod-based version of QObject::connect, introduced in Qt 4.8, to avoid some of these issues.
In your particular case:
You've got a typo in the function declaration: it's called seTUserID but you're using setUserID in the connect call.
You're using variable names, not function signatures, in your signal and slot names. Qt expects to see QObject::connect(&s_instance, SIGNAL(sendUserID(const QString), this, SLOT(setUserID(const QString))
You've got a signal connected to another signal, which is valid but doesn't do what you want (it's usually used to chain stuff like this: SomeChildWidget's signal -> MyClass1's signal -> MyClass2's slot).
Check that seTUserID definition is marked as slot. You are probably calling connect in some Data method (because you are using private member directly). Are you trying to use a ref to a pointer to s_instance O_o? Write sendUserID(const Qstring) (the signature of a signal) rather then sendUserID(uid). The same situation with setUserID. You are trying to connect to this pointer and want to send info to another class!? Use new style of Qt connecting signals and slots (pointers to members), with was introduced in Qt 5. Check for setUserID has written right.
I'm writing a tcp based server using Qt.
I plan this server to be multithreaded,
so my tcpserver class inherits from QTcpServer and overrides incomingConnection(). Everything is fine, except when it comes to deleting a user.
The TcpServer class manages
a list of QSharedPointer<Client>. When I remove the said client from the list, it gets automatically deleted because of the smart pointer.
My Client class owns a QSharedPointer<QTcpSocket> which means that the client's
QTcpSocket gets deleted when the client is deleted.
Problem is, it seems that Qt tries to use this socket after its deletion, causing Segmentation Fault.
Should I manages a list for the sockets only, and call deleteLater() on them when I dont need them anymore?
Or should I switch my socket's pointer in client class to a normal pointer?
void SlotSocketError(void)
{
QTcpSocket sock = qobject_cast<QTcpSocket *>(QObject::sender());
QSharedPointer<Client> client = GetClientFromSocket(sock);
_clientList.removeAt(GetClientPositionInList(client));
}
QList<QSharedPointer<Client> > _clientsList; // From TcpServer header.
/* Client's class header */
QSharedPointer<QTcpSocket> _socket;
You need to use deleteLater on the Object. Incoming messages may come in after you delete the QTCPSocket. It is documented in Assistant. You can find an example here: qthelp://com.trolltech.qt.472/qdoc/qt4-network.html
M
When you create your QSharedPointers you can pass a deleter to them, so that they'll use deleteLater instead of delete when you remove them from the list.
There is an example that does exactly that in the documentation too:
http://doc.trolltech.com/latest/qsharedpointer.html#QSharedPointer-3