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!
Related
I'm trying to send some data from QLocalSocket to QLocalSever in a loop. Sever only gets the first data and not receiving subsequent data, but if I introduce 1 mec delay between each call from the client then the server starts to receive everything. Please check out the below Client & Server code.
client.cpp
#include "client.h"
#include "QDataStream"
#include <QTest>
TestClient::TestClient() : m_socket{new QLocalSocket(this)}{
m_socket->connectToServer("TestServer");
if (m_socket->waitForConnected(1000)) {
qDebug("socket Connected!");
}
connect(m_socket, &QLocalSocket::readyRead, this, &TestClient::onNewData);
}
void TestClient::onNewData() {
qCritical() << "data received from server";
}
void TestClient::sendDataToServer() {
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_5_10);
QString testString = "test data";
out << quint32(testString.size());
out << testString;
m_socket->write(block);
m_socket->flush();
}
void TestClient::startClient() {
for(int i = 0; i < 5; i++) {
//QTest::qWait(1); //works if I uncomment this line
sendDataToServer();
}
}
server.cpp
#include "server.h"
TestServer::TestServer() : m_server{new QLocalServer(this)} {
QLocalServer::removeServer("TestServer");
if (!m_server->listen("TestServer")) {
qCritical() << "couldn't connect to server";
}
connect(m_server, &QLocalServer::newConnection, this, &TestServer::onNewConnection);
}
void TestServer::onNewConnection() {
m_socket = m_server->nextPendingConnection();
connect(m_socket, &QLocalSocket::readyRead, this,
&TestServer::onNewData);
connect(m_socket, &QLocalSocket::disconnected, m_socket,
&QLocalSocket::deleteLater);
}
void TestServer::onNewData() {
QLocalSocket* client = qobject_cast<QLocalSocket*>(sender());
client->readAll();
qCritical() << "data read by server";
}
from the qt doc, it's stated that
readyRead() is not emitted recursively; if you reenter the event loop
or call waitForReadyRead() inside a slot connected to the readyRead()
signal, the signal will not be reemitted (although waitForReadyRead()
may still return true).
so is this my problem? adding timer is the only solution here?
You can compile this test project -> http://www.filedropper.com/testsocket
I'm new with Qt Creator and I'm trying to read data from a light sensor communicating by I2C. I made a class PortListener that should return data received on the console once called.
PortListener::PortListener(const QString &portName)
{
this->port = new QSerialPort();
port->setPortName(portName);
port->setBaudRate(QSerialPort::Baud9600);
port->setDataBits(QSerialPort::Data8);
port->setParity(QSerialPort::NoParity);
port->setStopBits(QSerialPort::OneStop);
port->setFlowControl(QSerialPort::NoFlowControl);
port->open(QIODevice::ReadWrite);
QByteArray readData = port->readAll();
qDebug() << "message:" << readData;
}
But the only message I have is:
QIODevice::read (QSerialPort): device not open
message: ""
I don't understand what that mean?
1.Open the serialport,then set the parameters.
PortListener::PortListener(const QString &portName)
{
this->port = new QSerialPort();
port->open(QIODevice::ReadWrite);
port->setPortName(portName);
port->setBaudRate(QSerialPort::Baud9600);
port->setDataBits(QSerialPort::Data8);
port->setParity(QSerialPort::NoParity);
port->setStopBits(QSerialPort::OneStop);
port->setFlowControl(QSerialPort::NoFlowControl);
}
2.Connect the readyRead signal to a slot, and the slot is like this.
void PortListener::readyReadSlot()
{
while (!port.atEnd()) {
QByteArray data = port.readAll();
}
}
This is much more like the QextSerialPort, the following is the code from my application.
void SpClient::start()
{
myComClient = new QextSerialPort(Setting::devCom);
if(myComClient->open(QIODevice::ReadWrite))
{
qDebug() << "open " << Setting::devCom << "as client success";
}
myComClient->setBaudRate(BAUD9600);
myComClient->setDataBits(DATA_8);
myComClient->setParity(PAR_NONE);
myComClient->setStopBits(STOP_1);
myComClient->setFlowControl(FLOW_OFF);
myComClient->setTimeout(50);
....
}
My guess is your code is failing to open the serial port, I have run into permissions issues under linux opening USB ports. You will just need to do a chmod to grant your $USER access mostly.
I am receiving data in this shape:
Q1\n
9.70E-6\n
OK>
from an external device via QSerialPort, but with my reading routine
QString request = "Hello";
qDebug() << "TransAction started!";
QByteArray requestData = request.toLocal8Bit();
qDebug() << "Writing data: " << requestData;
serial->write(requestData);
qDebug() << "Data written";
if(serial->waitForBytesWritten(waitTimeout))
{
if(serial->waitForReadyRead(waitTimeout))
{
qDebug() << "Waiting for data!";
QByteArray responseData = serial->readAll();
while(serial->waitForReadyRead(100))
responseData += serial->readAll();
responseData.replace('\n', ' ');
QString response(responseData);
QByteArray response_arr = response.toLocal8Bit();
qDebug() << "Response is: " << response_arr.toHex();
emit this->response(response);
}
else
{
qDebug() << "Wait read response timeout";
emit this->timeout(tr("Wait read response timeout %1").arg(QTime::currentTime().toString()));
}
}
else
{
qDebug() << "Wait write request timeout!";
emit this->timeout(tr("Wait write request timeout %1").arg(QTime::currentTime().toString()));
}
I only get
Q1
as response. How can I modify my code such that I am able to read all input data?
Update:
When testing it with the serial port script described here: https://stackoverflow.com/a/7654527/2546099, everything works. Apparently the problem is that the qt-version stops reading after the first line break. This problem consists also if I add
char buffer[1000];
for(int i = 0; i < 1000; i++)
{
int tmp = serial->read(buffer, 1000);
if(tmp > 0)
qDebug() << buffer;
}
directly after the line
qDebug() << "Waiting for data!";
Then I still only get the first line (without the \n). Changing times does not change the received data.
The answer to my problem is (partly) described in this question: External vs internal declaration of QByteArray. My problem (why I did not receive any further data) was that I did not send a \x00D after the input line, thus the device just echoed my input, and was waiting for the Enter afterwards. After the input looks exactly as the first line, I misunderstood it for just getting the first line, and nothing else.
I am creating a simple threaded TCP server (based on the threaded Fortune server example). I have connected the readyRead signal to my readCommand slot, and I confirmed that the readCommand function fires after I've telnetted to my server and sent a string (followed by enter).
The function below outputs "In readCommand" once I send the string HELLO, then the output "new inBuffer" always shows empty ("").
void FortuneThread::readCommand()
{
qDebug() << "in readCommand" << endl;
QDataStream in(tcpSocketPtr);
in.setVersion(QDataStream::Qt_4_0);
in >> inBuffer;
qDebug() << "new inBuffer: " << inBuffer << endl;
...
}
If I print out tcpSocket->bytesAvailable(), then I see a growing count of characters as I send more via telnet. I'm just not getting them out of the socket...the code above is copied from the Fortune Client example so I assumed it would work. Am I using QDataStream wrong?
You should not initialize your QDataStream with QTcpSocket.
You should read the data from the socket io device with QByteArray QIODevice::readAll().
You should write the byte array of the previous operation into the data stream with the "<<" operator.
So, the code should look something like this:
void FortuneThread::readCommand()
{
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out << tcpSocketPtr->readAll();
...
}
The codes like these would work:
QByteArray res;
res.reserve(m_tcp_socket_ptr->bytesAvailable());
res = m_tcp_socket_ptr->readAll();
qDebug() << res;
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
}