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;
Related
I am trying to run the following code:
std::string RequestLogin()
{
std::cout << ">> User Login method..." << std::endl;
QNetworkRequest request(this->serverUrl);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QJsonObject payload;
payload.insert("userName", this->user);
payload.insert("password", this->password);
QByteArray byte_payload = QJsonDocument(payload).toJson();
QEventLoop loop;
bool a = connect(&(this->manager), SIGNAL(finished(QNetworkReply*)), &loop, SLOT(quit()));
// send post call
std::cout << "sending the post call" << std::endl;
QNetworkReply *reply = (this->manager).post(request, byte_payload);
loop.exec();
QByteArray reponse = reply->readAll();
std::cout << reponse.toStdString() << std:: endl;
return response.toStdString();
}
This method runs perfectly when I compile it as a c++ file with QT. But I am facing issue while build the wasm and calling this method. The event loop's quit slot is not called and so the response that I am waiting to get is not received.
The browser becomes unresponsive. On firefox, when I get notification to kill/wait for the page to respond, if I click kill, then in network tab I see the network rest completed successfully.
In the browser window the console did not get the response printed.
Does anybody know how to solve this issue?
For my requirement, I need to create methods in C++ which does some rest call, and on the response I need to do some computation before returning the values to Javascript.
Next examples work on small html pages, but dont work if page return big html data or json
I try this after loadFinished signal
view->page()->toHtml([cc](const QString &result){
qDebug() << "result ready";
qDebug() << "result string ="<<result;
});
I see "result ready", but second string not print, also I try this:
v->page()->runJavaScript("function jsfun(){return document.getElementsByTagName('html')[0].outerHTML;};");
//v->page()->runJavaScript("alert(jsfun())"); //work correctly
v->page()->runJavaScript("jsfun();",[this](const QVariant &v) {
qDebug() << "result ready";
qDebug() << "result string ="<<v.toString();
});
but again dont see second string
All example work correctly, but if buffer very big qDebug dont print data, you can write data to file or print part of data
Example (call toHtml after loadFinished signal) :
view->page()->toHtml([cc](const QString &result){
QFile file("outerHTML.html");
if(!file.open(QFile::WriteOnly | QFile::Text)){
qDebug() << "Cannot create a file";
return;
}
QTextStream stream(&file);
stream << buff;
file.close();
}
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!
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.
In the fortuneserver sample of Qt, a QString is sent by the method sendFortune(). Therefore one QString is selected from the QStringList fortunes:
QByteArray block;
QDataStream out(&block, QIODevice::WriteOnly);
out.setVersion(QDataStream::Qt_4_0);
out << (quint16)0;
out << fortunes.at(qrand() % fortunes.size());
out.device()->seek(0);
out << (quint16)(block.size() - sizeof(quint16));
QTcpSocket *clientConnection = tcpServer->nextPendingConnection();
clientConnection->write(block);
Is it also possible to send another type of data, like files, images or multiple strings? Or is it just possible to send a single string?
My second question: What is the advantage of sending data as QByteArry and why do I have to define (quint16) by setting up the QDataStream?
You don't send the data as QDataStream, QDataStream is a class that impersonates a stream, a way to transfer data, like wire.
QByteArray represents a storage for your data.
So, you can send data as QByteArray.
You can try QTcpSocket's member function called "int write(QByteArray)", like in the example you provided. Just take the image, the file, any other data and convert it to QByteArray. Here is where you will need QDataStream. Bind the stream to bytearray like this.
QByteArray dat;
QDataStream out(&dat, QIODevice::WriteOnly);
and use out to fill dat.
out << myImage << myImage2;
When you had finished filling the QByteArray, send it:
mySocket.write(dat);
don't forget to check the return value.
Read the docs and you will succeed.
To know if you have read all the data send by the other side of the socket, I use the commitTransaction() function from QDataStream:
Client::Client()
{
....
connect(tcpSocket, &QIODevice::readyRead, this, &Client::readData);
....
}
void Client::readData()
{
in.startTransaction();
QString data;
in >> data;
if (!in.commitTransaction())
{
qDebug() << TAG << "incomplete: " << data;
// readyRead will be called again when there is more data
return;
}
// data is complete, do something with it
....