QTcpSocket does not send data sometimes - qt

I have two QT apps. One app can be considered to hold a big data and it sends about 10 KB of data chunk every second to second application.
Earlier I tried using QUdpSocket to transmit the data but due to MTU limitation of about 2-5K and need to divide and reunite the data myself, I switched to QTcpSocket.
Sometimes data is sent correctly with QTcpSocket (especially if I write data very frequently ~every 100 ms) but sometimes data is not sent at all. Not even after 5 sec. And sometimes several data chunks are internally buffered for a long period (several seconds) and then sent together.
m_socket = new QTcpSocket(this);
m_socket->connectToHost(QHostAddress::LocalHost, 45454);
sendDataEverySec()
{
QByteArray datagram(10000, 'a');
qint64 len = m_socket->write(datagram);
if(len != datagram.size())
qDebug() << "Error"; //this NEVER occurs in MY case.
m_socket->flush();
}
On receiver side, I use readyRead signal to know when data has arrived.
How can I ensure that data is sent immediately? Are there any better alternatives for what I am trying to do?
Edit:: When I am writing after long gaps of 1 second, I receive "QAbstractSocket::SocketTimeoutError" on receiver side everytime sender sends data. This error is not received if sender writes data frequently.
Is it OKAY to use QTcpSocket to stream data the way I am doing????
Edit 2: On receiver side, when readyRead signal is emitted, I was again checking while(m_socket->waitForReadyRead(500)) and I was getting "QAbstractSocket::SocketTimeoutError" due to this. Also, this check was preventing delivering of single chunks.
After going through docs more, it seems readyRead will be continuously emitted when new data is available, so no need for waitForReadyRead.
I am receiving all data sent but still data does not come immediately. Sometimes two-three chunks are merged. This may be because of delay on receiver side in reading data etc.

On receiver side, when readyRead signal is emitted, I was again checking while(m_socket->waitForReadyRead(500)) and I was getting "QAbstractSocket::SocketTimeoutError" due to this. Also, this check was preventing delivering of single chunks.
After going through docs more, it seems readyRead will be continuously emitted when new data is available, so there is no need for waitForReadyRead.
It had solved my issue.

my tipical solution for client server app.
on server side :
class Server: public QTcpServer {
public:
Server(QObject *parent = 0);
~Server();
private slots:
void readyRead();
void disconnected();
protected:
void incomingConnection(int);
};
on cpp:
void Server::incomingConnection(int socketfd) {
QTcpSocket *client = new QTcpSocket(this);
client->setSocketDescriptor(socketfd);
connect(client, SIGNAL(readyRead()), this, SLOT(readyRead()));
connect(client, SIGNAL(disconnected()), this, SLOT(disconnected()));
}
void Server::disconnected() {
QTcpSocket *client = (QTcpSocket*) sender();
qDebug() << " INFO : " << QDateTime::currentDateTime()
<< " : CLIENT DISCONNECTED " << client->peerAddress().toString();
}
void Server::readyRead() {
QTcpSocket *client = (QTcpSocket*) sender();
while (client->canReadLine()) {
//here i needed a string..
QString line = QString::fromUtf8(client->readLine()).trimmed();
}
}
on client:
class Client: public QTcpSocket {
public:
Client(const QHostAddress&, int, QObject* = 0);
~Client();
void Client::sendMessage(const QString& );
private slots:
void readyRead();
void connected();
public slots:
void doConnect();
};
on cpp:
void Client::readyRead() {
// if you need to read the answer of server..
while (this->canReadLine()) {
}
}
void Client::doConnect() {
this->connectToHost(ip_, port_);
qDebug() << " INFO : " << QDateTime::currentDateTime()
<< " : CONNESSIONE...";
}
void Client::connected() {
qDebug() << " INFO : " << QDateTime::currentDateTime() << " : CONNESSO a "
<< ip_ << " e PORTA " << port_;
//do stuff if you need
}
void Client::sendMessage(const QString& message) {
this->write(message.toUtf8());
this->write("\n"); //every message ends with a new line
}

Related

Qt Multi-Thread Queued Connection Signal / Slot Issue (SLOT does not trigger)

Struggling emitting a signal from main/QML thread to another thread with a QList< QStringList > parameter. Variations I'v tried:
Q_DECLARATIVE_METATYPE in and out
Using EventExport vs. const EventExport& in signal and slot profiles
Sending empty EventExport in prepareExport() so emit has no/low data amount
Checking connect statement (always returns true)
Having qDebug() in prepareExport() and signal always appears to be emitted
Calling emit right after connect as a test (Works! Think you're going to tell me the main thread or cryoUtility objects don't exist but they do!)
Tried qRegisterMetaType with () and ("EventExport")...some say use text for typedef types
Any thoughts much appreciated!
sqlquery_model.h (not certain I need Q_DECLARATIVE_METATYPE but tried with and without...no change)
typedef QList<QStringList> EventExport;
Q_DECLARE_METATYPE(EventExport);
Q_INVOKABLE void prepareExport();
signals:
void updateEventListDataSig(const EventExport&);
sqlquery_model.cpp (this is connected to a qml page using TableView model...this emit does not seem to work)
void SqlQueryModel::prepareExport() {
if (this->rowCount() > 0) {
EventExport eventsList;
for(int i=0; i<this->rowCount(); ++i) {
QStringList eventInfo;
eventInfo.append(this->record().value(0).toString());
eventInfo.append(this->record().value(1).toString());
eventInfo.append(this->record().value(2).toString());
eventInfo.append(this->record().value(3).toString());
eventInfo.append(this->record().value(4).toString());
eventsList.append(eventInfo);
}
emit updateEventListDataSig(eventsList);
qDebug() << "Emit updatedEventListData" << eventsList.count();
}
}
main.cpp (includes sqlquery_model.h, need this as cryoUtility is a separate thread using Qt::QueuedConnection)
// Use string if using typedef method
qRegisterMetaType<EventExport>("EventExport");
mediator.h
void updateEventListDataSig(const EventExport&);
mediator.cpp (connects mainly live here, this test event works)
bool ret = connect(this, SIGNAL(updateEventListDataSig(const EventExport&)), cryoUtility, SLOT(updateEventListData(const EventExport&)), Qt::QueuedConnection);
EventExport ed;
emit updateEventListDataSig(ed);
qDebug() << "Event list CONN: " << ret;
utilities.h
void updateEventListData(const EventExport&);
utilities.cpp (this is the slot, trigger once on test call)
void Utilities::updateEventListData(const EventExport& el) {
qDebug() << "Load event list: ";// << el.count();
//eventList = el;
}
So, after more study, sqlmodelquery connection would have to occur in its constructor as it isn't active yet until its QML page loads.

How to send messages to connected clients from server in Qt

I know that how to send messages to the newly connected client whenever the QTcpServer newConnection is emitted. What I did is like this:
connect(serverConnection.tcpServer, &QTcpServer::newConnection, this, &ServerInterface::sendMessages);
void ServerInterface::sendMessages()
{
QByteArray messagesToClients;
QDataStream out(&messagesToClients, QIODevice::WriteOnly);
QTcpSocket *clientConnection = serverConnection.tcpServer->nextPendingConnection();
out << inputBox->toPlainText(); //get text from QTextEdit
clientConnection->write(messagesToClients);
}
But what I want to do is whenever the send messages button is clicked in the server, it will send messages to currently connected clients. The code I provide can only send only one new message to the newly connected client. I have no idea of how to achieve what I want to do, so can someone provide me a way to do that? I am kind of new to Qt networking.
Thanks.
Just store your connections in container. Like this:
in your ServerInterface h-file:
class ServerInterface {
// your stuff
public slots:
void onClientConnected();
void onClientDisconnected();
private:
QVector<QTcpSocket *> mClients;
};
in your ServerInterface cpp-file :
connect(serverConnection.tcpServer, SIGNAL(newConnection(), this, SLOT(onClientConnected());
void ServerInterface::onClientConnected() {
auto newClient = serverConnection.tcpServer->nextPendingConnection();
mClients->push_back( newClient );
connect(newClient, SIGNAL(disconnected()), this, SLOT(onClientDisconnected());
}
void ServerInterface::onClientDisconnected() {
if(auto client = dynamic_cast<QTcpSocket *>(sender()) {
mClients->removeAll(client);
}
void ServerInterface::sendMessages() {
out << inputBox->toPlainText();
for(auto &client : mClients) {
client->write(messagesToClients);
}
}

read and write to qtcpsocket using qdatastream

There are various methods of reading and writing from a QTcpSocket using a QDatastream as seen here
The difference is, I will be sending more than "one packet" or blocks.
A basic implementation on the server (sending) side and client (recieving) is seen below - only the actual sending and receiving snippets are shown
More Info, What I tried:
When writing to a QTcpSocket, I attempted to use the QTcpSocket::canReadLine() however this fails straightup after the QTcpSocket::waitForReadReady() signal fires.
I then tried QDataStream::atEnd() in a while loop which causes a system crash :(
The code below shows my latest attempt of going through the QDataStream docs, and utilzing the commitTransaction where it states
If no full packet is received, this code restores the stream to the initial position, after which you need to wait for more data to arrive.
Under the heading Using Read Transactions. But ofcourse, this just reads one block that is sent, i.e the first block.
Question:
When writing to a QTcpSocket multiple times, and flushing the socket each time to send that data, how can I read this from a QTcpSocket as it is send, keep the original "send structure"?
The example below only reads the first block and ends. I would like to read the block containing "Response 2" and "Response 3".
Code Implementations:
//server.h
//...
QTcpSocket *clientSocket = nullptr;
QDataStream in;
//...
//server.cpp
//...
in.setDevice(clientSocket);
in.setVersion(QDataStream::Qt_4_0);
in.startTransaction();
QString nextFortune;
in >> nextFortune;
if (in.commitTransaction())
ui->lblOut->setText(nextFortune);
if (clientSocket != nullptr) {
if (!clientSocket->isValid()) {
qDebug() << "tcp socket invalid";
return;
}
if (!clientSocket->isOpen()) {
qDebug() << "tcp socket not open";
return;
}
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << QString(QString("Response:") + nextFortune);
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
// block.clear();
out << QString("Response number 2");
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
// block.clear();
out << QString("Response number 3 here, and this is the end!");
if (!clientSocket->write(block)){
QMessageBox::information(this, tr("Server"),tr("Could not send message"));
}
clientSocket->flush();
clientSocket->disconnectFromHost();
}
//...
And the client side
//client.h
//...
QTcpSocket *tcp_con = nullptr;
QDataStream in;
//...
//client.cpp
//...
if(!tcp_con->waitForReadyRead()){
qDebug(log_lib_netman_err) << "tcp con timeout for reading";
tcp_con->disconnectFromHost();
return ReturnObject(ReturnCode::SocketError, QString());
}
in.setDevice(tcp_con);
in.setVersion(QDataStream::Qt_4_0);
in.startTransaction();
QList<QString> data_rcv = QList<QString>();
QString s;
// while (tcp_con->canReadLine()) {
// in >> s;
// data_rcv.push_back(s);
// }
// while (!in.read) {
in >> s;
data_rcv.push_back(s);
// }
while (!in.commitTransaction()){
qDebug(log_lib_netman_info) << "waiting for more data";
in >> s;
data_rcv.push_back(s);
// qDebug(log_lib_netman_err) << "Unable to send data to server";
// tcp_con->disconnectFromHost();
// return ReturnObject(ReturnCode::FailedReceiving, QString());
}
// if (s.isEmpty()) {
// qDebug(log_lib_netman_err) << "Empty response recieved";
// tcp_con->disconnectFromHost();
// return ReturnObject(ReturnCode::NoDataRecieved, QString());
// }
tcp_con->disconnectFromHost();
return ReturnObject(ReturnCode::ReceivedSuccess, data_rcv);
Help would be greatly appreciated!

QT QTcpServer::incomingConnection(qintptr handle) not firing?

I'm trying to create a multithreaded server using Qt for the first time. Normally one would use the socket pointer returned by the QTcpServer::nextPendingConnection() with the socket handle already baked in - but since I'm interfacing with the connecting client on a separate thread, I need to create the socket separately using the qintptr handle from QTcpServer::incomingConnection(qintptr handle). After a very dreary, error-packed debugging session I managed to track down the problem to the QTcpServer::incomingConnection() never being fired?
Has anyone had a similar problem - has something changed over recent versions Qt?
These are the ones I've tried:
QTcpServer::incomingConnection(qintptr handle)
QTcpServer::incomingConnection(qintptr socketDescriptor)
QTcpServer::incomingConnection(int handle)
EDIT:
Creating instance of server:
TestServer *myServer = new TestServer();
myServer->initializeServer(1234);
Which calls:
void TestServer::initializeServer(quint16 port)
{
mainServer = new QTcpServer(this);
mainServer->listen(QHostAddress::Any, port);
qDebug() << "Listening for connections on port: " << port;
}
Server is now listening. When a client connects incomingConnection(qintptr handle) is supposed to be called:
void TestServer::incomingConnection(qintptr socketDescriptor){
TestClient *client = new TestClient(this);
client->setSocket(socketDescriptor);
}
Which calls:
void TestClient::setSocket(quint16 socketDescr)
{
socket = new QTcpSocket(this);
socket->setSocketDescriptor(socketDescr);
connect(socket, SIGNAL(connected()),this,SLOT(connected()));
connect(socket, SIGNAL(disconnected()),this,SLOT(disconnected()));
connect(socket, SIGNAL(readyRead()),this,SLOT(readyRead()));
}
Called on connect() signal:
void TestClient::connected()
{
qDebug() << "Client connected..."; // This debug never appears in the console, since QTcpServer::incomingConnection isn't being fired.
}
You have some errors at your code:
At TestServer your QTcpServer probably aggregated, but you need to inherit it. At this case you try to override incomingConnection() method, but you haven't base class and you just create new incomingConnection(), not override.
You get qintptr descriptor variable from incomingConnection(), but set quint16 type at setSocket() method.
You probably mix client of server and client of your part, which just get incoming connection and handling socket data.
I write some little example below for your understanding tcp client-server communication.
Server part
Main part is server themselves:
#include <QTcpServer>
class TestServer: public QTcpServer
{
public:
TestServer(QObject *parent = 0);
void incomingConnection(qintptr handle) Q_DECL_OVERRIDE;
};
Just look: I don't aggragate QTcpServer, but inherited it. At this case you can override incomingConnection() method correctly.
At constructor we just start server for listening using listen() method:
TestServer::TestServer(QObject *parent):
QTcpServer(parent)
{
if (this->listen(QHostAddress::Any, 2323)) {
qDebug() << "Server start at port: " << this->serverPort();
} else {
qDebug() << "Start failure";
}
}
Then time for overriding incomingConnection():
void TestServer::incomingConnection(qintptr handle)
{
qDebug() << Q_FUNC_INFO << " new connection";
SocketThread *socket = new SocketThread(handle);
connect(socket, SIGNAL(finished()), socket, SLOT(deleteLater()));
socket->start();
}
I create SocketThread object which handle incoming data:
#include <QThread>
#include <QObject>
class QTcpSocket;
class SocketThread: public QThread
{
Q_OBJECT
public:
SocketThread(qintptr descriptor, QObject *parent = 0);
~SocketThread();
protected:
void run() Q_DECL_OVERRIDE;
private slots:
void onConnected();
void onReadyRead();
void onDisconnected();
private:
QTcpSocket *m_socket;
qintptr m_descriptor;
};
We inherits from QThread for making our server multithreading, so we have to override run() method:
SocketThread::SocketThread(qintptr descriptor, QObject *parent)
: QThread(parent), m_descriptor(descriptor)
{
}
void SocketThread::run()
{
qDebug() << Q_FUNC_INFO;
m_socket = new QTcpSocket;
m_socket->setSocketDescriptor(m_descriptor);
connect(m_socket, SIGNAL(readyRead()), this, SLOT(onReadyRead()), Qt::DirectConnection);
connect(m_socket, SIGNAL(disconnected()), this, SLOT(onDisconnected()), Qt::DirectConnection);
exec();
}
This way we initialize our QTcpSocket, set socket descriptor, connect it with readyRead() and disconnected() signals and start event loop.
void SocketThread::onReadyRead()
{
QDataStream in(m_socket);
in.setVersion(QDataStream::Qt_5_5);
QString message;
in >> message;
qDebug() << message;
m_socket->disconnectFromHost();
}
void SocketThread::onDisconnected()
{
m_socket->close();
// Exit event loop
quit();
}
At onReadyRead() just read some QString from client, write it to console and disconnect from host. At onDisconnected() we close socket connection and exit event loop.
Client part
It is just example and bad-smells style, but i create connection to the server at MainWindow class on QPushButton::clicked signal:
void MainWindow::on_pushButton_clicked()
{
QTcpSocket *client = new QTcpSocket;
connect(client, SIGNAL(connected()), this, SLOT(connected()));
connect(client, SIGNAL(disconnected()), client, SLOT(deleteLater()));
client->connectToHost(QHostAddress::LocalHost, 2323);
client->waitForConnected();
if (client->state() != QAbstractSocket::ConnectedState ) {
qDebug() << Q_FUNC_INFO << " can't connect to host";
delete client;
return;
}
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_5);
out << QString("Hello");
out.device()->seek(0);
client->write(block);
}
void MainWindow::connected()
{
qDebug() << Q_FUNC_INFO << " client connected";
}
I create new QTcpSocket, connect it to the signals and try to connect to the host(in my case it is localhost). Wait for connected and check socket status. If all is right I write to socket QString - just example.
It is one of possible ways to organized multithreading client-server architecture, i hope it will be helpfull for you and you find yours bugs.

Read output of multiple write in QProcess one by one

Can anyone help me read the output of qprocess after write and loop until all task is done?
I have this code
wifi->write("scan\n");
wifi->closeWriteChannel();
wifi->waitForBytesWritten(100);
wifi->waitForReadyRead(100);
wifi->waitForFinished(100);
qDebug() << "read output" << wifi->readAllStandardOutput();
wifi->write("scan\n");
wifi->closeWriteChannel();
wifi->waitForBytesWritten(100);
wifi->waitForReadyRead(100);
wifi->waitForFinished(100);
qDebug() << "read output" << wifi->readAllStandardOutput();
the expected output must be
"OK"
"scan results"
but the ouput is
"OK"
""
thanks.
Your multiple waits are not useful for anything. All you care about is when the process finishes, so have a single waitForFinished call with a much longer timeout (those scans don't happen in ~100ms, a few seconds is a good minimum).
You should not be using the blocking waitForXxx methods. They trip up everyone and are a source of unending grief. Forget that they exist. Use process's signals to react to events as they happen.
Qt 5 + C++11
This is the way forward. This is why you should insist on using a modern development environment, if you can. It's less typing and easier to understand.
void MyObject::startWifi() {
auto process = new QProcess(this);
process->start("program", QStringList() << "argument");
connect(process, &QProcess::started, [process]{
process->write("scan\n");
process->closeWriteChannel();
});
connect(process, &QProcess::finished, [process]{
qDebug() << process->readAllStandardOutput();
process->deleteLater();
});
}
Qt 4
class MyObject : public QObject {
Q_OBJECT
QProcess m_wifi;
Q_SLOT void onStarted() {
m_wifi.write("scan\n");
m_wifi.closeWriteChannel();
}
Q_SLOT void onFinished() {
qDebug() << m_wifi.readAllStandardOutput();
}
public:
MyObject(QObject * parent = 0) : QObject(parent) {
connect(&m_wifi, SIGNAL(started()), SLOT(onStarted()));
connect(&m_wifi, SIGNAL(finished(int,QProcess::ExitStatus)),
SLOT(onFinished()));
}
Q_SLOT void start() {
m_wifi.start("program", QStringList() << "argument");
}
};
Then invoke the start method/slot on an instance of this object. That's all.

Resources