How to use QtcpSocket properly in two way communication.
-
Hi. I am developing application and server that are cooperating together. Server is measurement station with scanner on robot arm. Application requests measurement, server orders robot to run measurements, and then even over 1 million points with all of its data is send back. I created TCP socket and server as I show below. I am waiting for all data to came in. And here is the problem. I want to communicate server status, to allow user to stop scanning and lock other functionality. The problem is when I send server status, then tone of data and then server status again it looks like data get messed up over network. When I do it with debugging it's all good, it have time to process everything.
Is there any way to make this exchange safe? Now I have computer and server on one PC, but sending data over Wi-Fi will be nightmare… I was thinking about chopping data into packets myself, to add package ID and a serial number.
I guess that subject is more complicated so link to more extensive tutorial or even book would be nice.connction.h
class Connection : public QObject { Q_OBJECT public: explicit Connection(); ~Connection(); bool isConnected() { return m_idConnection; bool connectToServer(QString ip, quint16 port, QString sessionName); void disconnect(bool close = false); void requestFastScan(); ServerStateEnum getServerStatus(){ return m_serverState; } [...] private: QTcpSocket m_tcpSocket; bool m_idConnection; quint64 m_uiBlockSize; unsigned char m_uiCurrentScanType; ServerStateEnum m_serverState; void sendXMLData(QString &xmlData); // bunch of cloud realated things private Q_SLOTS : void onConnected(); void onDisconect(); void readFromServer(); void resetTCPConnection(); }; #endif
connction.cpp
void Connection::requestFastScan() { if (m_tcpSocket.isOpen()) { QString xmlQS; QXmlStreamWriter textWriter(&xmlQS); //m_xmlWriter.setDevice(&m_tcpSocket); textWriter.writeStartDocument(); textWriter.writeStartElement(SCAN_QSTR); textWriter.writeStartElement(FAST_QSTR); // close tag fast textWriter.writeEndElement(); // end tag scan textWriter.writeEndElement(); // end document textWriter.writeEndDocument(); scanState.setWaitingForScan(true); sendXMLData(xmlQS); } } void Connection::readFromServer() { QDataStream in(&m_tcpSocket); in.setVersion(QDataStream::Qt_5_4); quint64 bytesAvailable = m_tcpSocket.bytesAvailable(); m_app->dispToConsole(QString("[qRobotAppPlugin] Reading data from server, %1!").arg(bytesAvailable), ccMainAppInterface::STD_CONSOLE_MESSAGE); if (m_uiBlockSize == 0) { if (bytesAvailable < (int)(sizeof(quint64) + sizeof(unsigned char))) return; in >> m_uiBlockSize; in >> m_uiCurrentScanType; //m_timer->singleShot(2000, this, SLOT(resetTCPConnection)); } if (bytesAvailable < m_uiBlockSize) return; if (m_uiCurrentScanType == SERVER_STATE) { unsigned char serverState; QString msg; in >> serverState; in >> msg; m_serverState = (ServerStateEnum) serverState; if (msg.length() > 0) { m_app->dispToConsole(msg, ccMainAppInterface::ERR_CONSOLE_MESSAGE); } m_uiBlockSize = 0; return; } //processing could data [...] } void Connection::sendXMLData(QString &xmlData) { QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_4); // ************************************* DATA *************************************** out << (quint64)0; out << XML_DATA;// unsined short out << xmlData; quint64 size = (quint64)(block.size() - sizeof(quint64) - sizeof(unsigned char)); out.device()->seek(0); out << size; m_tcpSocket.write(block); m_tcpSocket.flush(); m_tcpSocket.waitForBytesWritten(); m_serverState = ServerStateEnum::NotReady; }
And now server:
tcpserver.h
class TCPServer : public QTcpServer { Q_OBJECT public: explicit TCPServer(QObject *parent = Q_NULLPTR); virtual ~TCPServer(); Q_SIGNALS: void closed(); void stopScanning(); private Q_SLOTS: void onNewConnection(); void socketDisconnected(); void readData(); void sendData(double* xyz, unsigned char* rgb, double* norm, int length, int colStepSender, int rowStepSender); private: QTcpSocket* m_connection; ScannerRobot* m_robot; QXmlStreamWriter m_xmlWriter; bool m_bRobotInitialize; quint64 m_uiBlockSize; unsigned char m_uiCurrentScanType; bool processXMLData(QString &xmlData, QString &msg); void sendServerStatus(ServerStateEnum _serverStatus, QString &_msg = QString()); };
tcpserver.cpp
void TCPServer::readData() { QDataStream in(m_connection); in.setVersion(QDataStream::Qt_5_4); quint64 bytesAvailable = m_connection->bytesAvailable(); if (m_uiBlockSize == 0) { if (bytesAvailable < (int)(sizeof(quint64) + sizeof(unsigned char))) return; in >> m_uiBlockSize; in >> m_uiCurrentScanType; } if (bytesAvailable < m_uiBlockSize) return; // ************************* DATA PROCESSING ***************************************** m_uiBlockSize = 0; if (XML_DATA != m_uiCurrentScanType) { std::wcout << L"What is this data type?" << std::endl; return; } QString xmlData; in >> xmlData; // HERE IS PROBLEM //sendServerStatus(ServerStateEnum::StopReady); QString msg; if (!processXMLData(xmlData, msg)) if (msg.length() == 0) msg = QString("Data processing finished with error. Please check server console for more info."); // HERE IS PROBLEM //sendServerStatus(ServerStateEnum::Ready, msg); } void TCPServer::sendServerStatus(ServerStateEnum _serverStatus, QString &_msg) { QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_4); out << (quint64)0;//data length for later for later out << SERVER_STATE; out << (unsigned int)_serverStatus; if (_msg.length() > 0) out << _msg; //writing size at the beginning (now is later) quint64 size = (quint64)(block.size() - sizeof(quint64) - sizeof(unsigned char)); out.device()->seek(0); out << size; m_connection->write(block); m_connection->flush(); m_connection->waitForBytesWritten(); } void TCPServer::sendData(double* xyz, unsigned char* rgb, double* norm, int length, int colStepSender, int rowStepSender) { QByteArray block; QDataStream out(&block, QIODevice::WriteOnly); out.setVersion(QDataStream::Qt_5_4); // ************************************* XYZ *************************************** out << (quint64)0;//data length for later for later out << XZY_DATA; for (quint64 i = 0; i<length; ++i) { out << xyz[i]; } //writing size at the beginning (now is later) quint64 size = (quint64)(block.size() - sizeof(quint64) - sizeof(unsigned char)); out.device()->seek(0); out << size; m_connection->write(block); m_connection->flush(); m_connection->waitForBytesWritten(); // then rest if data [...] }
-
Did you check out already the network examples?
Especially the fortune server and client example is in my opinion a very good example.With both examples you may test also over WiFi as it s your concern. However, a TCP connection shall ensure in general that the information send is also received on the other side. This is part of the protocol. TCP some sort of handshaking to acknowledge the reception of the information. If not successful a packet will be repeatably send until it has been received respectively the connection will break up.
Your sendServerStatus creates apparently the problem. You need to send the size of the string, but you simply send 0.
The decoder on the other side needs to know where information starts and ends. Either you scan there for fixed information strings (not recommended for different strings with different length) or you have to send the number of string bytes in advance. On the other side you will know the number of bytes to expect and read a string its proper length. -
I did check it out. I have similar model for my server - socket system. The problem is, I send tone of data, and they get messed up. I have no idea how to synchronize everything.
And about sendServerStatus function - I am overriding 0:out << (quint64)0;//data length for later for later out << SERVER_STATE; out << (unsigned int)_serverStatus; if (_msg.length() > 0) out << _msg; //writing size at the beginning (now is later) quint64 size = (quint64)(block.size() - sizeof(quint64) - sizeof(unsigned char)); out.device()->seek(0); out << size;
-
@Pepek said:
I did check it out. I have similar model for my server - socket system. The problem is, I send tone of data, and they get messed up. I have no idea how to synchronize everything.
Not sure what you mean with "tone of data"? "tons of data" like large amounts?@Pepek said:
out << (quint64)0;//data length for later for later out << SERVER_STATE; out << (unsigned int)_serverStatus; if (_msg.length() > 0) out << _msg; //writing size at the beginning (now is later) quint64 size = (quint64)(block.size() - sizeof(quint64) - sizeof(unsigned char)); out.device()->seek(0); out << size;
OK, I did not see this. However, I do not understand right away why you are substracting also sizeof (unsigned char).
-
@Pepek said:
if (m_uiBlockSize == 0) {
if (bytesAvailable < (int)(sizeof(quint64) + sizeof(unsigned char)))
return;in >> m_uiBlockSize; in >> m_uiCurrentScanType; //m_timer->singleShot(2000, this, SLOT(resetTCPConnection));
}
It's because in
unsigned char
I have data type that is coming next. I take it into account every time, but truth be told, it is unnecessary to be in header.