I am writing a simple network application that will be sending and receiving TCP messages to a single listener. I use a context class to wrap much of the communication logic as such:
class context : public QObject
{
Q_OBJECT
public:
void setDevice (unique_ptr<QIODevice> device);
void sendMessage (message msg);
signals:
void newMessage (message msg);
private:
unique_ptr<QIODevice> m_device;
}
A generic QIODevice is given to the object instead of the concrete subtype QTcpConnection so that the logic can be tested without a network connection. I expected that any network errors would be presented as IO errors from the QIODevice. However, if the host disconnects, the QIODevice does not emit a signal. The QTcpConnection does emit a signal (disconnected) but the underlying QIODevice remains open, continues to allow reads and writes, and does not appear to give any notification of the error. My confirmation that the socket does see the disconnect is that the error string in QIODevice changes from "Unknown error" to "Socket is not connected"; but checking for an error string seems like a hack that shouldn't be necessary.
Is there actually some way to detect this kind of error with only the base class? If I am required to pass in the QTcpSocket, is there a way that I can still keep unit tests that do not require a client-server connection?
Related
I have a Qt-based library which has an object that emits signals, but also does some stuff asynchronously behind the scenes.
It is possible for the user of the library to connect the signal being emitted by the object using Qt::DirectConnection or Qt::BlockingQueuedConnection, which will hijack the object's dedicated thread and therefore, and potentially break its functionality.
Is there a way of only allowing for the signal to be connected using a given type of connection? Qt::QueuedConnection, to be specific. Such that the user gets a compilation error when they try to use any other type of connection.
You can use a proxy signal that you declare in the public interface and connect your own internal signals to that signal using queued connection. Thus, the user will be able to use this signal in any way (direct or queued), but it will be already emitted by queued connection.
Since signals can't be private, there is no way to hide them once they are declared in a class.
One solution is to use PIMPL to hide your internal signals from the user. The example shows only queued connection between signals, but not the PIMPL:
#include <QObject>
class InternalSignal : public QObject
{
Q_OBJECT
public:
explicit InternalSignal(QObject *parent = nullptr) : QObject{parent} {
connect(this, &InternalSignal::myPIMPLSignal, this, &InternalSignal::mySignal, Qt::QueuedConnection);
}
signals:
void mySignal();
void myPIMPLSignal(); // move this signal to PIMPL class
};
I'm working on some application using Qt framework, it uses serial port and need to receive some data, problem is that data is organized with some protocol, which i parse in readyRead() signal handler(slot), at the same time, i need to wait for parsed message with some timeout. So, to wait, i use QThread::msleep() after sending, but i've noticed, that when thread goes to sleep in msleep(), readyRead will be emitted only after msleep() will be finished. My question is, why readyRead can't wake up thread? Qt signal is not working like real Unix signals?
If you have single thread application readyRead slot not executes immediatly you got data on port. Read about Qt::QueuedConnection in QObject::connect(). When readyRead signal emits in serial port class it just added to queue of events (in global event loop). All events will be executed later by QEventLoop (inside QApplication instance).
Looks like you sleep your main thread so QEventLoop cant execute anything while sleeping. In this case you need to read your port from another thread to avoid main thread sleeping.
I'm trying to connect a D-Bus signal to a Qt slot. For example, I'm interested to catch the CallAdded signal:
http://git.kernel.org/cgit/network/ofono/ofono.git/tree/doc/voicecallmanager-api.txt
I did the folloging:
QDBusConnection::systemBus().connect(OFONO_SERVICE, m_modem.path(), OFONO_VOICECALLMANAGER_INTERFACE, "CallAdded", this, SLOT(callAdded(QString,QMap<QString,QVariant>)));
where:
OFONO_SERVICE = "org.ofono"
m_modem.path() = /hfp/org/bluez/hci0/dev_xx_xx_x_xx_xx_xx
OFONO_VOICECALLMANAGER_INTERFACE = "org.ofono.VoiceCallManager"
but nothing happens when I make or receive a call. Of course I'm connected and I've enabled and set online the modem.
Perhaps my "translated" signature is wrong?
I'm completely new at using the QtNetwork for connecting computers.
Right now all I want is to see an attempt at a connection. So I create a GUI application and on the mainwindow.cpp I write these two functions as slots for two buttons:
void MainWindow::on_pbTalk_clicked(){
QString IP = ui->leIP->text();
ui->pteLog->appendPlainText("Now Talking to IP: " + IP);
talker = new Talker();
talker->connectToHost(IP,25000);
}
void MainWindow::on_pbListen_clicked(){
ui->pteLog->appendPlainText("Now listening on any port, I think");
listener = new Listener(this);
if (!connect(listener, SIGNAL(newConnection()), this, SLOT(on_newConnections()))){
ui->pteLog->appendPlainText("The connection of slot and signal failed!");
}
}
Now Talker is essentially a QTcpSocket there is nothing reimplemented just yet.
Listener is a QTcpServer with the following code con Listener.cpp:
Listener::Listener(QObject *parent) :
QTcpServer(parent)
{
qDebug() << "Listening on any port";
listen(QHostAddress::Any);
}
void Listener::incomingConnection(int socketDescriptor){
qDebug() << "New connection: " << socketDescriptor;
}
So I run two instances of the same program. One is in my machine. I run the program and push the Listen button (IP 10.255.255.101).
The second instance is run in a virtual machine (IP 10.255.255.215) where I run the program and push the Talk button. This, as I understand should attempt to open a connection to IP (which is 10.255.255.101) at port 25000 and I should get a "New connection" message in the console. However no such message appears. And since this is not working, I'm not moving on.
Can any one tell me what I might be doing wrong?
Check the documenation of QTcpServer::listen - it says:
Tells the server to listen for incoming connections on address address
and port port. If port is 0, a port is chosen automatically. If
address is QHostAddress::Any, the server will listen on all network
interfaces.
QHostAddress::Any means that you are listening on all network interfaces, not ports. (For example, if you want to have a local server only, you could use QHostAddress::LocalHost - check QHostAddress::SpecialAddress for more like that.
If you want to set the port manually, you have to call:
listen(QHostAddress::Any, 25000);
If not, you can get the automatically chosen port by calling
quint16 port = serverPort();
The "listening on any port" idiom isn't available for your use; it's not how UDP and TCP were meant to be used. Most likely you shouldn't be designing your communications that way. Use a dedicated port.
If you want to build a packet sniffer, you'll have to use the platform-specific mechanisms designed for that. There are libraries that help you with that task, the most notable would be WinPcap on Windows and cross-platform libpcap from the TcpDump project.
I have a network application which uses UDP broadcasts for device discovery, but only accepts one connection at a time. So, when a new TCP connection is made, I delete the QUdpSocket that was used for discovery.
However, when the remote device is disconnected, I want to create a new QUdpSocket and start listening again:
// Set up a UDP server to respond to any "discovery" messages:
udpServer = new QUdpSocket(this);
if (udpServer -> bind(QHostAddress::Any, DISCOVERY_PORT))
connect(udpServer, SIGNAL(readyRead()),
this, SLOT(beDiscovered()));
else
{
fprintf(stderr, "UDP port not bound successfully: %d, ", udpServer ->error());
fprintf(stderr, udpServer ->errorString().toLocal8Bit());
fprintf(stderr, "\r\n");
fflush(stderr);
#ifdef WIN32
_commit(_fileno(stderr));
#else
fsync(_fileno(stderr));
#endif
}
The re-bind fails, however, with code 8, "The bound address is already in use".
So, how can I make sure that when the 'old' QUdpSocket was deleted, it fully releases the address(es) it was bound to?
Alternatievly, should I be binding with QUdpSocket::ShareAddress or QUdpSocket::ReuseAddressHint? This doesn't seem right, as neither really describe the behaviour I want, namely an exclusive binding for my QUdpSocket during its lifetime, and in any case QUdpSocket::ShareAddress is supposed to be the default on Windows.
Thanks,
Stephen.
...so in other words the question has answered itself!