In a small Qt application, I have a NetworkAccessManager(NAM) wrapped in a singleton class. NAM object of this class is used by four independent form classes. Each form class fires a request to a separate php (and php takes at least 3 seconds with sleep(3)).
Now there are two cases:
Case I: When NAM, for each form, is connected to form's slot IN CONSTRUCTOR.
In this case, when i send requests from all four forms simultaneously; all the responses are directed to just one form (the one that was first in firing the request) and not to the form that requested it.
Case II: When NAM is connected (for firing request) and disconnected (when response is received) to form's slot IN FUNCTIONS (and not constructor).
In this case, when i send requests from all four forms simultaneously (i.e. in less than 3 seconds); then only the first response is returned and the rest never returned. This is in accordance with the code as singleton NAM was disconnected as soon as it received the reply for the first request. Therefore other requests could not be processed.
All form classes are identical and use the same code. Here is the code (Case II specific):
void Request2::slotStartRequest(){
m_Req2 = NetworkAccessManager::getInstance();
connect(m_Req2, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotReplyFinished(QNetworkReply*)));
QString postData = "value=222";
m_Req2->post(QNetworkRequest(QUrl(path)), postData.toUtf8());
}
void Request2::slotReplyFinished(QNetworkReply *reply){
disconnect(m_Req2, SIGNAL(finished(QNetworkReply*)), this, SLOT(slotReplyFinished(QNetworkReply*)));
ui->TEdit_Req2->setText(reply->readAll());
reply->deleteLater();
}
Though the code for both cases are very similar. This code is Case II specific. For Case I, only change is that 'connect' and 'getInstance' codes are put in constructor (and there is no 'disconnect').
The code will work fine with one NetworkAccessManager object per form class.
But how do I achieve the desired behaviour (with one NAM for whole application) that response is directed only to the form that requested it and also the requests for other forms should not be affected ?
The NAM function post() creates a new QNetworkReply instance to manage that request. It is possible to connect directly to QNetworkReply signals instead of signals of QNetworkAccessManager:
void Request2::slotStartRequest(){
QString postData = "value=222";
QNetworkAccessManager *nam = NetworkAccessManager::getInstance();
m_Reply = nam->post(QNetworkRequest(QUrl(path)), postData.toUtf8());
connect(m_Reply, SIGNAL(finished()), this, SLOT(slotReplyFinished()));
}
Now only this instance of QNetworkReply *m_Reply can call the signal of this. Note that there is no QNetworkReply *reply argument in the finished() signal:
void Request2::slotReplyFinished(){
ui->TEdit_Req2->setText(m_Reply->readAll());
m_Reply->deleteLater();
}
My suggestion (what I do) is to keep track of which object made the request on the manager, perhaps update your manager to take in the object pointer
m_Req2->post(QNetworkRequest(QUrl(path)), postData.toUtf8(), this);
Then on the Manager, use that pointer and use the QMetaObject to invoke the method, instead of emitting a signal: http://doc.qt.io/qt-5/qmetaobject.html#invokeMethod.
So you get something like this, on the Manager
QMetaObject::invokeMethod(obj, "slotReplyFinished", Qt::DirectConnection,
Q_ARG(QNetworkReply*, reply));
(not tested, but you should get the idea)
[edit: fixed code]
Related
I am trying to build a multithreaded server. The problem that I am encountering is that the emitted signal sends message to all clients instead to the specific one on a specific thread.
I tried to solve the issue by creating a QList of threads, but how do I connect them specifically?? When signal is emitted, it wakes up all my threads, the problem is getting worse by each connection, since the clients are dynamically allocated as they are connecting.
Code:
void NetworkServer::incomingConnection(qintptr socketDescriptor)
{
QThread *thread = new QThread;
threadhandle *session = new threadhandle;
QObject::connect(this, &NetworkServer::stopped, session, &threadhandle::close);
QObject::connect(this, &NetworkServer::stopped, thread, &threadhandle::quit);
QObject::connect(session, &threadhandle::closed, thread, &threadhandle::quit);
QObject::connect(thread, &QThread::started, this, &NetworkServer::threadStarted, Qt::DirectConnection);
QObject::connect(thread, &QThread::finished, this, &NetworkServer::threadFinished, Qt::DirectConnection);
QObject::connect(thread, &QThread::finished, session, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater);
//the problem is here, I can send message to all clients connected, but how to specifiy a thread/client???
QObject::connect(this, &NetworkServer::sendMsgToThread, session, &threadhandle::sendMsgToClient);
thread->start();
session->moveToThread(thread);
QMetaObject::invokeMethod(session, "open", Qt::QueuedConnection, Q_ARG(qintptr, socketDescriptor));
Update - 40 minutes later - Problem solved
I apologize for not providing more details, basically I am building a simple client-server relationship. Message is send from server GUI line in text format to a specific client.
After some thinking I came up with a solution.
so for each new thread, we will append its session to: QList<threadhandle*>sessionList
After that we are going to edit the sendMsgToClient()
// user clicks on Send Message button in GUI
void NetworkServer::sendMsgToClient(int clientNum, QString msg)
{
//connect the signal to a specific thread, clientNum is selected from a table in mainWindow GUI where the NetworkServer class is initiated
QObject::connect(this, &NetworkServer::sendMsgToThread, sessionList.at(clientNum), &threadhandle::sendMsgToClient);
//emit the signal to specific thread
emit sendMsgToThread(clientNum, msg);
//disconnect the signal, since it is no longer needed
QObject::disconnect(this, &NetworkServer::sendMsgToThread, sessionList.at(clientNum), &threadhandle::sendMsgToClient);
}
Hard to say really without MRE that builds and presents your exact problem. But, guessing from context: no clue how NetworkServer::sendMsgToThread or threadhandle::sendMsgToClient are implemented, but I guess you could store the thread ID/index/any other identifier and then pass it to specific thread only, possible via some proxy object?
Point is, sendMsgToThread gets connected to multiple slots, which is probably not what you want; instead those single session-thread pairs need to be somehow distinguished, e.g. by thread ID/its index in list, whatever.
I guess you could wrap the session and thread in some other class, that would be stored (and referred to) by the server by some of the aforementioned IDs, and it would forward the messages to the worker (session) object. But I cannot really tell without at least basic knowledge of your code's architecture.
I am going to send data collected in the QMainWindow to an object of QDialog via signal-slot connections. Here is the relevant code fragment to do this in mainwindow.cpp:
void MainWindow::on_dialogA_clicked()
{
if(dialogA==NULL) dialogA =new MyDialog();
//'this' refers to MainWindow
connect(this,SIGNAL(sendData(QVector<bool>)), dialogA, SLOT(getData(QVector<bool>)), Qt::QueuedConnection);
dialogA->show();
}
However when working with dialogA, it seems that the data are not updated properly and the dialog interface becomes Not responding after while. I wonder if the signal-slot connection above is right or not, or that is the way to have data communication with a QDiaglog.
Two things...first, switch to the modern method of creating signal/slot connections:
connect (this, &MainWindow::sendData, dialogA, &MyDialog::getData, Qt::QueuedConnection);
If there's something wrong with the definition, using this format allows the compiler to catch it rather than a run-time warning. Assuming the parameters are defined correctly, there's nothing wrong with the "connect" statement except that it's in the wrong place, which is the second issue.
Every time the user clicks, an additional connection is being made between your main window and the dialog. Qt doesn't automatically ensure that only one connection is made between a given signal and slot. It'll create as many as you ask it for. The "connect" call should be part of the "if" block:
if (! dialogA)
{
dialogA =new MyDialog();
connect...
}
Depending on how much data is in that vector and what the dialog does with it, if you click enough times, it may be that you're just processing the data so many times that everything slows down tremendously.
This question is an upgrade on the following two questions:
Qt Model View pattern, design choices for connecting Model with Data
Qt Model-View update view?
Here is the situation:
MODEL has a pointer to the SERVER (SERVER represents Data) through which it gets the required data and formats them into QStrings, so that the VIEW can understand them. The model keeps no internal copies of the QList, it accesses it directly and converts the requ QTcpSocket * to QStrings in the QVariant QAbstractItemModel::data method.
However the list of sockets can change without the Model or View knowing about it if a new connection to the SERVER is established. In that case another QTcpSOcket * is appended to the SERVERs QList.
How to notify the View on the Model/Data change?
Call QAbstractItemModel::reset() from the SERVER on each new connection. I consider this bad for it requires to modify the SERVER for the needs of the MODEL in which case i could of just had the MODEL and the SERVER as a single entity.
connect(&server, QTcpServer::newConnection, &model, &StationListModel::reset) Try to connect the SERVER and MODEL via Signals and Slots. However, &StationListModel::reset ISN'T a slot, so i believe this isn't the right way.
I would like to hear which of the mentioned approaches (if any) is considered appropriate in the given situation. And is the insisting on MODEL-SERVER loose coupling a bad design choice?
Here is how it should be done:
Create signals in SERVER that notify about data change (or use existing QTcpServer::newConnection signal if it is sufficient).
Create a slot (or slots) in your model class and connect SERVER's signal to this slot.
In the slot's implementation emit signals or call internal methods (e.g. beginInsertRows, endInsertRows) or just reset the model to notify the view about new changes.
Since you need to incrementally append new items to your view, I would do this in the following way:
In your model class
// A slot
void MyModel::onNewConnection()
{
// Append new socket to the list QList<QTcpSocket *>
m_socketList.puch_back(new QTcpSocket);
// Update the model.
insertRow(0);
}
// Virtual function
bool MyModel::insertRows(int row, int count, const QModelIndex &parent)
{
if (!parent.isValid()) {
// We need to append a row to the bottom of the view.
int rows = rowCount() - 1;
beginInsertRows(parent, rows, rows);
// Do nothing, but just inform view(s) that the internal data has changed
// and these rows should be added.
endInsertRows();
return true;
}
return QAbstractItemModel::insertRows(row, count, parent);
}
Somewhere in your code
[..]
connect(&server, QTcpServer::newConnection, &model, &StationListModel::onNewConnection)
I know this is an old question, but I would like to share what I do when I was working on the exactly same issue.
If you are placing the server pointer into the model implementation and you get all the model information from the QList< QTcpSocket *> use this connection:
connect(server, SIGNAL(newConnection()), this, SIGNAL(modelReset()));
Can we restrict QNetworkAccessManager from consuming whole bandwidth, by restricting the download speed, as we do see such options available with almost every download manager?
This is not possible out of the box. But have a look at the Qt Torrent Example, especially the class RateController (ratecontroller.h | ratecontroller.cpp). This class does almost what you want by controlling not only one but a set of connections.
However, this rate controller is operating on QTcpSockets (to be exact on PeerWireClients), so you need to change the type of the "peers" to be QIODevice, which I hope isn't that hard, since PeerWireClient inherits from QTcpSocket, which itself inherits from QIODevice:
// old
void addSocket(PeerWireClient *socket);
// new
void addDevice(QIODevice *device);
(Note that the RateController from the Torrent example controlls both upload and download, but you only need to control the download rate. So you can remove unnecessary code.)
Then you need to make requests made by your QNetworkAccessManager use this rate controller. This can be done by reimplementing QNetworkAccessManager and overwriting (extending) the method QNetworkAccessManager::createRequest, which will be called whenever a new request gets created. This method returns the QNetworkReply* (which inherits from QIODevice*) where the download will be read from, so telling the rate controller to control this device will limit the download rate:
QNetworkReply *MyNetworkAccessManager::createRequest(
QNetworkAccessManager::Operation op,
const QNetworkRequest &req,
QIODevice *outgoingData)
{
// original call to QNetworkAccessManager in order to get the reply
QNetworkReply *reply = QNetworkAccessManager::createRequest(op, req, outgoingData);
// add this reply (which is a QIODevice*) to the rate controller
rateController.addDevice(reply);
return reply;
}
You will not have to subclass QNetworkAccessManager if you already know the pieces of code where you actually perform requests. The methods get() and post() return a QNetworkReply* which you can also just add to the rate controller. (But this way, you manually do this outside of the manager, which is doesn't fulfill the concept of information/implementation hiding, in this case the fact that downloads are rate-controlled.)
Say I have a Qt application where I have something like this:
connect(A, SIGNAL(a()), B, SLOT(b1()));
connect(A, SIGNAL(a()), B, SLOT(b2()));
...
void B::b1() {
A->disconnect();
}
If a() is emitted, will the slot B::b2() still be called after disconnecting all slots from A in B::b1(), since they both respond to the same signal? Assume that both objects live in the same thread, so we have a direct connection.
I know that the disconnect() disconnects all signal connections from A, but I am not sure if the emit just schedules both the b1 and b2 slots to be called and then calls them, so that a change to the connections has no effect until the two slots (and therefore the emit) return. So it could be implemented like:
emit:
make temprorary copy of signal's slot table
foreach element in temporary slot table: call
disconnect:
clear signal's slot table
Otherwise you could run into datastructure integrity problems.
A quick experiment shows that the second slot is not called.
However, I'm not sure if this is something that Qt promises.
Qt does document that certain checks are made during the iteration of connected receivers, but it's but that's pretty loose and non-specific (and I haven't come across anything better in my short search); from http://doc.qt.io/qt-5/signalsandslots.html:
This is the overhead required to locate the connection object, to safely iterate over all connections (i.e. checking that subsequent receivers have not been destroyed during the emission)
So they loosely document that certain checks are made while a signal is emitted, such as whether a receiver has been destroyed. I think checking whether the connection has been disconnected seems like a similar kind of situation, but it would be nice if that were explicitly documented.
Since it sounds like this will be a problem for you (you want all the signals to get through, right?), you may be able to work around this problem by ensuring that the signal to slot b1() is connected last. Qt does promise that slots will be called by a signal in connection order, so if you arrange for the slot that does the disconnect to be the last one, all the other slots will be seen.
I don't know how easy this might be to arrange, but it also seems kind of strange that an object would disconnect everything from inside another object's slot function (so there's probably some other refactoring that can solve this problem as well).
If none of that is acceptable, it wouldn't be too hard to come up with a proxy object for A's signals that has the behavior you need. There would be a single:
connect(A, SIGNAL(a()), proxy_obj, SLOT(proxy_slot()));
to connect A's signal to the proxy, then B can connect to the proxy's signal (which the proxy would be emit when the proxy's slot got called):
connect(proxy_obj, SIGNAL(a()), B, SLOT(b1()));
connect(proxy_obj, SIGNAL(a()), B, SLOT(b2()));
Since A's disconnect will not affect the connections that the proxy has to B, the proxy will ensure that all of B's slots get called when signal A->a() is emitted.
class A_proxy : public QObject
{
Q_OBJECT
public slots:
void proxy_slot() {
emit a();
}
signals:
void a();
};
disconnect reference page answers your question.
It depends on how you call it, and the way you call it (without any parameter), it will disconnect all signals on object A, therefore the slot B:b2 will not be called after disconnect.