Manage stream client server with Qt - c++

I wonder how complex software client-server manage stream.
For example when I play counter strike, they are lots of packet send from client to server and server to clients. What is the best method to manage stream ?
I think :
Server send data to client
void Server::send(const QString &message)
{
QByteArray paquet;
QDataStream out(&paquet, QIODevice::WriteOnly);
out << (quint16) 0; //packet size
out << (quint8)14; //packet type to execute a special action on the client
out << message; // data, here string but can be another class
out.device()->seek(0);
out << (quint16) (paquet.size() - sizeof(quint16)); //write packet size
socket->write(paquet);
}
Client receive data from server
void Client::receiveData()
{
QDataStream in(socket);
if (size_packet == 0) // size_packet, initilize to 0
{
if (socket->bytesAvailable() < (int)sizeof(quint16))
return;
in >> tailleMessage;
}
if (socket->bytesAvailable() < tailleMessage)
return;
//now I can get data
quint8 typeObject;
in >> typeObject;
switch(typeObject){
case 1:
...
break;
case 2:
...
break;
case 3:
...
break;
...
...
case 14:
QString data; //can be another custom class
in >> data;
write(data);
break;
//my solution, the server can execute an action on the client depending type packet
}
size_packet= 0;
}
I guess I have to use a shared library between the client and the server and serialized my custom class.
But should I do otherwise? Use json or xml or protocol buffer (google) or something else ???

As #MrEricSir mention, games usually use UDP protocol. It uses datagrams, not streams. So some datagrams can be lost during data transmission. You protocol should work with data lost. Often such protocol are binary, they do not use xml, json or protobuf because of overhead.

Related

toLocal8bit send over TCP

Im creating TCP Server/Client application in QT Creator framework. I want to get some data from UI input field and send it over TCP.
Im doing something like this in client application:
void MainWindow::on_btn_login_clicked()
{
QByteArray text = (ui->login_input->text()).toLocal8Bit();
char* out = text.data();
connection->ConnectAndSendData(out);
}
and in the ConnectAndSendData function:
void TcpConnect::ConnectAndSendData(const char* data)
{
socket = new QTcpSocket(this);
int port = 1234;
socket->connectToHost("localhost", port);
if(socket->waitForConnected(3000))
{
qDebug() << "connected to s. localhost at port " << port;
socket->flush();
socket->write(data, sizeof(data));
qDebug() << data << "\n";
socket->waitForReadyRead();
char* serverresponse;
socket->read(serverresponse, 128);
if(serverresponse == MESSAGE_LOGINRQ)
socket->write(data);
socket->flush();
socket->close();
}
else
{
/**/
}
}
and the data in line socket->write(data, sizeof(data)); is properly send to server, but when server echoes it, it looks like "something/x00/x00/x00/x00" or somethinglike that. Also when i to do something like this:
#define MESSAGE_WANTLOGIN "wanlogin"
socket->write(MESSAGE_WANTLOGIN, sizeof(MESSAGE_WANTLOGIN));
message is messed up with those null signs.
on the server side receiving data look as simple as:
void Thread::readyRead()
{
socket->flush();
QByteArray data = socket->readAll();
qDebug() << "data received: " << data;
if(data == MESSAGE_WANTLOGIN)
{
socket->write(MESSAGE_LOGINRQ);
} else
{
qDebug() << "error not messageloginrq";
}
}
and like u can assume, though i send "wanlogin" message, server receiving something like "wanlogin/x00/x00" and this if obviously returns false.
this trash is applied on the end of data, and this impossible to check what message was send. The other thing is that maximum size of send data is 8 chars, but also to data of this length trash is applied so it looks like "wanlogin/x00/x00"; however, when i type more chars, for example 10, the send data is just cut to 8 signs, with no /x00s.
So my question is how to clear data from those /x00s and how to send more than 1 byte of information(i need it e.g. to send login and password of user). Sorry if there's some stupid mistake, its my first client/server application which also using multithreading for each client.
sizeof(data) is 4 or 8 depending if you are on a 32-bit or 64-bit machine. It is not the size of your data, but the size (in byte) of a pointer.
So what happens is that your actual wanlogin is in fact a 6 character string, and you end up sending 2 more bytes. In this case you are lucky: the char array returned by data() is null-terminated, so you have one extra 0 that you can access, but accessing the second 0 is undefined behavior i.e anything can happen.
The solution is to use strlen() instead of sizeof. Or, better, to directly call write() with a QByteArray by changing ConnectAndSendData(const char* data) to ConnectAndSendData(const QByteArray &data).
void MainWindow::on_btn_login_clicked()
{
const QByteArray text = (ui->login_input->text()).toLocal8Bit();
connection->ConnectAndSendData(text);
}
void TcpConnect::ConnectAndSendData(const QByteArray & data)
{
socket = new QTcpSocket(this);
quint16 port = 1234;
socket->connectToHost("localhost", port);
if(socket->waitForConnected(3000))
{
qDebug() << "connected to s. localhost at port " << port;
socket->write(data);
...
}
...
}

qt c++ send 6 bytes using tcp protocol

Hi everybody, first of all I'm new on network programming so maybe this is a simple question, but I don't get it. I try to send 6 Bytes to a microcontroller using tcp socket. In order to serialize my data I'm using qdatastream and qbytearray. That's the way I try to do this:
QByteArray buffer;
QDataStream outputStream(&buffer, QIODevice::WriteOnly);
outputStream.setVersion(QDataStream::Qt_5_8);
outputStream << (quint8) 0
<< (quint8) dataMessage.prefix
<< (quint8) dataMessage.paramID
<< (quint32) dataMessage.data;
outputStream.device()->seek(0);
outputStream << (quint8)(buffer.size() - sizeof(quint8));
qDebug() << buffer;
qDebug() << quint32(dataMessage.data);
sock->write(buffer);
if(sock->write(buffer) == -1)
{
return -1;
}
sock->flush();
My struct dataMessage looks like this for example: dataMessage={15,1,9400000}
Well in my buffer I would expect this Byte sequence "\x06\xF0\x01\x00\x8F\6E\xC0"
but I always get the following Output "\x06\xF0\x01\x00\x8Fn\xC0".
What am I doing wrong?

Reading from TCP socket

I have to receive data from TCP socket. It has fixed 16-bytes header (one header's field is data length) and data. I receive it with BigEndian, but it was send with LittleEndian.
I can' find good solution for such a data reception. What is working for me now is (reading one of the header fields):
QByteArray packetType = tcpSocket->read(2);
QDataStream in(packetType);
in.setByteOrder(QDataStream::LittleEndian);
quint16 pT = 0;
in >> pT;
Is there any better way to set QByteArray endianess?
Or a way to read specified number of bytes with QDataStream?
You're almost doing it right. The intermediate QByteArray is unnecessary. You can set the data stream directly on the socket:
QTcpSocket socket;
QDataStream in(&socket);
in.setByteOrder(QDataStream::LittleEndian);
...
void onReadyRead() {
in.startTransaction();
uint16_t packet_type;
in >> packet_type;
if (in.status == QDataStream::Ok) {
if (packet_type == Foo) {
uint32_t foo_data;
in >> foo_data;
if (! in.commitTransaction())
return; // not enough data
// got a full packet here
}
}
in.abortTransaction();
}

Unable to read correctly from socket

I have a server application which sends some xor encrypted strings. I am reading them from my QT client application. Sometimes, the server is slower and I am not able to receive the entire string. I have tried something like below but it gets stuck ( see the comment below). How can I wait until I have the entire data. I tried bytesAviable() but then again i get stuck (infinite loop)
QTcpSocket * sock = static_cast<QTcpSocket*>(this->sender());
if (key == 0)
{
QString recv(sock->readLine());
key = recv.toInt();
qDebug() << "Cheia este " << key;
char * response = enc_dec("#AUTH|admin|admin",strlen("#AUTH|admin|admin"),key);
sock->write(response);
}
else
{
busy = true;
while (sock->bytesAvailable() > 0)
{
unsigned short word;
sock->read((char*)(&word),2);
qDebug()<<word;
//Sleep(100); if i do this than it works great!
QByteArray bts = sock->read(word);
while (bts.length() < word)
{
char bit; //here get's stuck
if (sock->read(&bit,1) > 0)
bts.append(bit);
sock->flush();
}
char * decodat = enc_dec((char*)bts.data(),bts.length() - 2,key);
qDebug() << decodat;
}
}
I don't know what the meaning of key == 0 is, but you are almost certainly misusing available(), like almost everybody else who has ever called it, including me. It tells you how much data can be read without blocking. It has nothing to do with how much data may eventually be delivered down the connection, and the reason is that there are TCP APIs that can tell you the former, but not the latter. Indeed the latter doesn't have any real meaning, considering that the peer could keep writing from now until Doomsday. You should just block and loop until you have read the amount of data you need for the next piece of work.
I offer you to do the following:
QObject::connect(this->m_TCPSocket, SIGNAL(readyRead()), this, SLOT(processRecivedDatagrams()));
Some explanation:
It is convinient to create a class instance of which will manage network;
One has the member which is pointer on TCPSocket;
In constructor implement connection of signal from socket readyRead() which is emmited when needed data was delivered with SLOT(processRecivedDatagrams()). which is responsible for processing recived datagrams/ in this case it is processRecivedDatagrams(), also implement this slot
Mind that class which manages network has to inherit from QObject and also in its declaration include macrosQ_OBject` for MOC.
update:
i also offer you to store recived data in container like stack or queue this will allow you to synhronize sender and reciver (container in this case acts like buffer)
// SLOT:
void Network::processRecivedDatagrams(void)
{
if (!this->m_flagLocked) // use analog of mutex
{
this->m_flagLocked = true; // lock resource
QByteArray datagram;
do
{
datagram.resize(m_TCPSocket->pendingDatagramSize());
m_TCPSocket->readDatagram(datagram.data(), datagram.size());
}
Qt::String YourString; // actualy I don`t remember how to declare Qt string
while (m_TCPSocket->hasPendingDatagrams());
QDataStream in (&datagram, QIODevice::ReadOnly);
in >> YourString
--numberOfDatagrams;
}
this->m_flagLocked = false; // unlock resource
}
}

Simplest QT TCP client

I would like to connect to a listening server and transmit some data. I looked at the examples available but they seem to have extra functions that do not seem very helpful to me (i.e. connect, fortune, etc.). This is the code I have so far:
QTcpSocket t;
t.connectToHost("127.0.0.1", 9000);
Assuming the server is listening and robust, what do I need to implement to send a data variable with datatype QByteArray?
very simple with QTcpSocket. Begin as you did...
void MainWindow::connectTcp()
{
QByteArray data; // <-- fill with data
_pSocket = new QTcpSocket( this ); // <-- needs to be a member variable: QTcpSocket * _pSocket;
connect( _pSocket, SIGNAL(readyRead()), SLOT(readTcpData()) );
_pSocket->connectToHost("127.0.0.1", 9000);
if( _pSocket->waitForConnected() ) {
_pSocket->write( data );
}
}
void MainWindow::readTcpData()
{
QByteArray data = pSocket->readAll();
}
Be aware, though, that for reading from the TcpSocket you may receive the data in more than one transmission, ie. when the server send you the string "123456" you may receive "123" and "456". It is your responsibility to check whether the transmission is complete. Unfortunately, this almost always results in your class being stateful: the class has to remember what transmission it is expecting, whether it has started already and if it's complete. So far, I haven't figured out an elegant way around that.
In my case I was reading xml data, and sometimes I would not get all in one packet.
Here is an elegant solution. WaitForReadyRead could also have a time out in it and
then some extra error checking in case that timeout is reached. In my case I should never
receive an incomplete xml, but if it did happen this would lock the thread up indefinetly
without the timeout:
while(!xml.atEnd()) {
QXmlStreamReader::TokenType t = xml.readNext();
if(xml.error()) {
if(xml.error() == QXmlStreamReader::PrematureEndOfDocumentError) {
cout << "reading extra data" << endl;
sock->waitForReadyRead();
xml.addData(sock->readAll());
cout << "extra data successful" << endl;
continue;
} else {
break;
}
}
...