QT with AMQP-CPP library
-
wrote on 18 May 2023, 20:24 last edited by
What is the best way to integrate QT QML application to connect with RabbitMQ server using the following library - https://github.com/CopernicaMarketingSoftware/AMQP-CPP
From high-level description of the library, it seems like a qthandler class that inherits from AMQP::TcpHandler with monitor function needs to be implemented to interact with QT event loop. However, I am unable to use QSocketNotifier to make this work. I am using C++17 and Qt version 5.1.5. Any suggestions?
-
What is the best way to integrate QT QML application to connect with RabbitMQ server using the following library - https://github.com/CopernicaMarketingSoftware/AMQP-CPP
From high-level description of the library, it seems like a qthandler class that inherits from AMQP::TcpHandler with monitor function needs to be implemented to interact with QT event loop. However, I am unable to use QSocketNotifier to make this work. I am using C++17 and Qt version 5.1.5. Any suggestions?
wrote on 19 May 2023, 00:13 last edited by maragr@maragr I was able to get it to work by providing this class as handler to the AMQP::TcpConnection function in main. However, I see that
onSocketActivated
function is called multiple times by Qt, even though the data is not present on the socket. What is the reason for that and how to resolve this?#ifndef MYTCPHANDLER_H #define MYTCPHANDLER_H #include <QObject> #include <QSocketNotifier> #include <QTcpSocket> #include <amqpcpp.h> #include <amqpcpp/linux_tcp.h> class MyTcpHandler : public QObject, public AMQP::TcpHandler { Q_OBJECT public: explicit MyTcpHandler(QObject *parent = nullptr); signals: private slots: void onSocketActivated(int fd, QSocketNotifier::Type flags); private: int m_fd; int m_flags; AMQP::TcpConnection* _conn; QSocketNotifier* socketNotifier; virtual void monitor(AMQP::TcpConnection* connection, int fd, int flags); virtual void onError(AMQP::TcpConnection* connection, const char* message); }; #endif // MYTCPHANDLER_H
#include "mytcphandler.h" MyTcpHandler::MyTcpHandler(QObject *parent) : QObject{parent}, AMQP::TcpHandler() { } void MyTcpHandler::onSocketActivated(int fd, QSocketNotifier::Type flags) { qDebug() << "Reached process"; _conn->process(fd, AMQP::readable); } void MyTcpHandler::monitor(AMQP::TcpConnection *connection, int fd, int flags) { qDebug() << "Reached monitor"; qDebug() << fd; m_fd = fd; m_flags = flags; _conn = connection; socketNotifier = new QSocketNotifier(m_fd, QSocketNotifier::Read); connect(socketNotifier, &QSocketNotifier::activated, this, &MyTcpHandler::onSocketActivated); socketNotifier->setEnabled(true); } void MyTcpHandler::onError(AMQP::TcpConnection *connection, const char *message) { qDebug() << message; }
-
@maragr I was able to get it to work by providing this class as handler to the AMQP::TcpConnection function in main. However, I see that
onSocketActivated
function is called multiple times by Qt, even though the data is not present on the socket. What is the reason for that and how to resolve this?#ifndef MYTCPHANDLER_H #define MYTCPHANDLER_H #include <QObject> #include <QSocketNotifier> #include <QTcpSocket> #include <amqpcpp.h> #include <amqpcpp/linux_tcp.h> class MyTcpHandler : public QObject, public AMQP::TcpHandler { Q_OBJECT public: explicit MyTcpHandler(QObject *parent = nullptr); signals: private slots: void onSocketActivated(int fd, QSocketNotifier::Type flags); private: int m_fd; int m_flags; AMQP::TcpConnection* _conn; QSocketNotifier* socketNotifier; virtual void monitor(AMQP::TcpConnection* connection, int fd, int flags); virtual void onError(AMQP::TcpConnection* connection, const char* message); }; #endif // MYTCPHANDLER_H
#include "mytcphandler.h" MyTcpHandler::MyTcpHandler(QObject *parent) : QObject{parent}, AMQP::TcpHandler() { } void MyTcpHandler::onSocketActivated(int fd, QSocketNotifier::Type flags) { qDebug() << "Reached process"; _conn->process(fd, AMQP::readable); } void MyTcpHandler::monitor(AMQP::TcpConnection *connection, int fd, int flags) { qDebug() << "Reached monitor"; qDebug() << fd; m_fd = fd; m_flags = flags; _conn = connection; socketNotifier = new QSocketNotifier(m_fd, QSocketNotifier::Read); connect(socketNotifier, &QSocketNotifier::activated, this, &MyTcpHandler::onSocketActivated); socketNotifier->setEnabled(true); } void MyTcpHandler::onError(AMQP::TcpConnection *connection, const char *message) { qDebug() << message; }
-
wrote on 19 May 2023, 17:01 last edited by maragr
@ChrisW67
Looks likeMyTcpHandler::monitor
got called 3 times in the beginning with 2 errors and then once it found a successful connection, it doesn't get called again.
However, theMyTcpHandler::onSocketActivated
got called multiple times in the beginning and then "precisely" 6 times before every message. Below is the log.QML debugging is enabled. Only use this in a safe environment. Reached monitor 36 Reached process Reached monitor 40 Reached monitor 36 QSocketNotifier: Invalid socket 36 and type 'Read', disabling... Reached process Reached process QSocketNotifier: Invalid socket 36 and type 'Read', disabling... Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process "amq.ctag-eS23ghYSNLT_UywYko8agg" Threaded rendering is not optimal in the Mapbox GL plugin. [ INFO ] "{QSGRenderThread}[General]: GPU Identifier: Mesa Intel(R) UHD Graphics (TGL GT1)" Reached process Reached process Reached process Reached process Reached process Reached process 1 < Print of message 1 body> Reached process Reached process Reached process Reached process Reached process Reached process 2 < Print of message 2 body> Reached process Reached process Reached process Reached process Reached process Reached process .......
-
@ChrisW67
Looks likeMyTcpHandler::monitor
got called 3 times in the beginning with 2 errors and then once it found a successful connection, it doesn't get called again.
However, theMyTcpHandler::onSocketActivated
got called multiple times in the beginning and then "precisely" 6 times before every message. Below is the log.QML debugging is enabled. Only use this in a safe environment. Reached monitor 36 Reached process Reached monitor 40 Reached monitor 36 QSocketNotifier: Invalid socket 36 and type 'Read', disabling... Reached process Reached process QSocketNotifier: Invalid socket 36 and type 'Read', disabling... Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process Reached process "amq.ctag-eS23ghYSNLT_UywYko8agg" Threaded rendering is not optimal in the Mapbox GL plugin. [ INFO ] "{QSGRenderThread}[General]: GPU Identifier: Mesa Intel(R) UHD Graphics (TGL GT1)" Reached process Reached process Reached process Reached process Reached process Reached process 1 < Print of message 1 body> Reached process Reached process Reached process Reached process Reached process Reached process 2 < Print of message 2 body> Reached process Reached process Reached process Reached process Reached process Reached process .......
Hi,
That's where your issue lies. You don't delete the previous socket notifier before creating a new one. So you have now three active notifiers.
-
Hi,
That's where your issue lies. You don't delete the previous socket notifier before creating a new one. So you have now three active notifiers.
-
@SGaist So how do I catch any errors and delete the socket notifiers that are not required?
You should keep track of the file descriptors and if already in use, don't create a new QSocketNotifier for it.
-
You should keep track of the file descriptors and if already in use, don't create a new QSocketNotifier for it.
wrote on 20 May 2023, 00:18 last edited by maragr@SGaist Not sure if that is the case. Because the
QSocketNotifier: Invalid socket 36 and type 'Read', disabling...
shows that the invalid socket is disabled. So it might not be firing theactivated
signal. I printed out the file descriptor whenonSocketActivated
gets triggered and all of them are called by the same fd. (in this case fd=40). What am I missing?Same behavior is observed even when I keep track of all file descriptors and only create new ones when they are not in use. The only difference with this addition is that now the error
QSocketNotifier: Invalid socket 36 and type 'Read', disabling...
only appears once. -
@SGaist Not sure if that is the case. Because the
QSocketNotifier: Invalid socket 36 and type 'Read', disabling...
shows that the invalid socket is disabled. So it might not be firing theactivated
signal. I printed out the file descriptor whenonSocketActivated
gets triggered and all of them are called by the same fd. (in this case fd=40). What am I missing?Same behavior is observed even when I keep track of all file descriptors and only create new ones when they are not in use. The only difference with this addition is that now the error
QSocketNotifier: Invalid socket 36 and type 'Read', disabling...
only appears once.wrote on 20 May 2023, 08:50 last edited by@maragr
Hi. Don't know if I can help butting in. Seems I don't understand what you are asking as well as the others do. Which issue are you trying to address now?- Multiple calls to
onSocketActivated()
when no data? - Message
Invalid socket ...
? - Keeping track of which file descriptor numbers you have placed notifiers on?
- Something else?
- Multiple calls to
-
@maragr
Hi. Don't know if I can help butting in. Seems I don't understand what you are asking as well as the others do. Which issue are you trying to address now?- Multiple calls to
onSocketActivated()
when no data? - Message
Invalid socket ...
? - Keeping track of which file descriptor numbers you have placed notifiers on?
- Something else?
wrote on 20 May 2023, 17:08 last edited by@JonB i am only trying to address why the onSocketActivated function is being called multiple times even when there is no data?
(The comments above indicated that it might be an issue with creating many socket notifiers per file descriptor. But resolving this by tracking file descriptors and socket notifiers does not solve the above issue. Hope this helps!)
- Multiple calls to
-
@JonB i am only trying to address why the onSocketActivated function is being called multiple times even when there is no data?
(The comments above indicated that it might be an issue with creating many socket notifiers per file descriptor. But resolving this by tracking file descriptors and socket notifiers does not solve the above issue. Hope this helps!)
wrote on 20 May 2023, 17:58 last edited by JonB@maragr
OK, now I know where we are :)The
QSocketNotifier
uses Linuxselect()
call on the file descriptor. I really think you should assume if it says there is a Read data available when it's called there is. Assuming we are indeed not talking about multi-notifiers per descriptor, then I would question how you get your assertion/evidence that no data is there. -
@maragr
OK, now I know where we are :)The
QSocketNotifier
uses Linuxselect()
call on the file descriptor. I really think you should assume if it says there is a Read data available when it's called there is. Assuming we are indeed not talking about multi-notifiers per descriptor, then I would question how you get your assertion/evidence that no data is there.wrote on 20 May 2023, 21:33 last edited by maragr@JonB Hmm that is a good question. My assertion was based on the fact that
onSocketActivated
was called 6 times before theconsume.onReceived
function was triggered from the AMQP-CPP library, thus reading the available message. But now, I think there might be something with this function_conn->process(fd, AMQP::readable);
(called within the onSocketActivated slot) from AMQP-CPP library that does not register theread
call right away onceactivated
signal is received.Is this a correct assumption that
QSocketNotifier
will keep triggering theactivated
signal until the data is read from the socket? -
@JonB Hmm that is a good question. My assertion was based on the fact that
onSocketActivated
was called 6 times before theconsume.onReceived
function was triggered from the AMQP-CPP library, thus reading the available message. But now, I think there might be something with this function_conn->process(fd, AMQP::readable);
(called within the onSocketActivated slot) from AMQP-CPP library that does not register theread
call right away onceactivated
signal is received.Is this a correct assumption that
QSocketNotifier
will keep triggering theactivated
signal until the data is read from the socket?wrote on 21 May 2023, 07:48 last edited by JonB@maragr
I was about to say "no", I thought it would only raise the signal whenever new data arrived, even if old data was left there presently unread. I believe that is what Qt's normal socketreadyRead()
behaves:This signal is emitted once every time new data is available for reading from the device. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device
However, I am not sure that will be case for
QSocketNotifier
usingselect()
(or maybepoll()
) call. That returns whenever any data is available on a file descriptor (so that aread()
will complete), I don't think it would know that new data has arrived rather than old data is already lying there.Unless one or both of these reads the data into a buffer when it first arrives, so that the descriptor is clear for future reads.
Truth is I don't know, you would have to test.
-
@maragr
I was about to say "no", I thought it would only raise the signal whenever new data arrived, even if old data was left there presently unread. I believe that is what Qt's normal socketreadyRead()
behaves:This signal is emitted once every time new data is available for reading from the device. It will only be emitted again once new data is available, such as when a new payload of network data has arrived on your network socket, or when a new block of data has been appended to your device
However, I am not sure that will be case for
QSocketNotifier
usingselect()
(or maybepoll()
) call. That returns whenever any data is available on a file descriptor (so that aread()
will complete), I don't think it would know that new data has arrived rather than old data is already lying there.Unless one or both of these reads the data into a buffer when it first arrives, so that the descriptor is clear for future reads.
Truth is I don't know, you would have to test.
-