PDA

View Full Version : Mixing QUdpSocket and Winsocks - Weird output



ecamargo
9th February 2018, 17:11
Hey guys,

I am not sure what is going on here or if the issue could be easily fixed or not. It might be a basic thing that I am missing.
I need to send some data over network UDP sockets so I built a "sender" and a "receiver" using QUdpSocket which worked fine.
The requirements changed and now the "sender" has to send data using Winsocks instead QUdpSocket but the "receiver" still can used QUdpSocket.
I am getting a weird output when using qDebug on the received stream. I have put together an example about how I am doing everything and
hopefully, someone will have the answer.



######### SENDING - #1
stringstream str;

str << (uint32)0;

str << (uint32)0x53486472 <<
(uint32) << 0
(uint32)1 <<
(uint32)2 <<
(uint32)3 <<
(uint32)4 <<
(uint32)5 <<
(uint32)6 <<
(uint32)7;

str << (uint32)0x54695355 <<
(char*)INFO-1 <<
(char*)869543-1 <<
(char*)85A-1 <<
(uint16)1 <<
(char*)C0B3-MV1R-1 <<
(bool)false;



char * sendbuf = new char[str.str().length() + 1];
std::strcpy(sendbuf, str.str().c_str());


struct sockaddr_in clientService;
clientService.sin_family = AF_INET;
clientService.sin_addr.s_addr = inet_addr(address);
clientService.sin_port = htons(port);


SOCKET sock = socket(clientService.sin_family, SOCK_DGRAM, IPPROTO_UDP);
connect(sock, (SOCKADDR*)&clientService, sizeof(clientService));
send(sock, sendbuf, (int)strlen(sendbuf), 0);
closesocket(sock);



######### SENDING - #2

QByteArray datagram;
QDataStream stream(&datagram, QIODevice::WriteOnly);
stream.setVersion(QDataStream::Qt_5_9);

stream << (uint32)0;

stream << (uint32)0x53486472 <<
(uint32) << 0
(uint32)1 <<
(uint32)2 <<
(uint32)3 <<
(uint32)4 <<
(uint32)5 <<
(uint32)6 <<
(uint32)7;

stream << (uint32)0x54695355 <<
(char*)INFO-1 <<
(char*)869543-1 <<
(char*)85A-1 <<
(uint16)1 <<
(char*)C0B3-MV1R-1 <<
(bool)false;


QUdpSocket *udpSocket = new QUdpSocket(this);
udpSocket->writeDatagram(datagram.data(), datagram.size(), QHostAddress::LocalHost, port);



######### RECEIVING

QUdpSocket *udpSocket = new QUdpSocket(this);

QByteArray datagram;
qint64 size = udpSocket->pendingDatagramSize();
datagram.resize(size);
udpSocket->readDatagram(datagram.data(), datagram.size());

QDataStream stream(&datagram, QIODevice::ReadOnly);
stream.setVersion(QDataStream::Qt_5_9);

qDebug() << "NetworkReceiver";
qDebug() << datagram.size();
qDebug() << datagram;





-------- Output when using SENDING #1:
NetworkReceiver
61
"01397253234012345671416188757INFO-1869543-185A-11C0B3-MV1R-10"




-------- Output when using SENDING #2:
NetworkReceiver
97
"\x00\x00\x00\x00SHdr\x00\x00\x00\x00\x00\x00\x00\x 01\x00\x00\x00\x02\x00\x00\x00\x03\x00\x00\x00\x04 \x00\x00\x00\x05\x00\x00\x00\x06\x00\x00\x00\x07Ti SU\x00\x00\x00\x07INFO-1\x00\x00\x00\x00\t869543-1\x00\x00\x00\x00\x06""85A-1\x00\x00\x01\x00\x00\x00\fC0B3-MV1R-1\x00\x00"

Ginsengelf
12th February 2018, 06:55
Hi, I think the difference is that in "SENDING - #1" you use a stringstream while in "SENDING - #2" you use a QDataStream. The QDataStream will encode data depending on the version used in setVersion.

Ginsengelf

ChrisW67
14th February 2018, 04:14
QDataStream outputs sufficient information to reassemble the result at the other end.
The uint32 values are 4 bytes each in a byte dictated by settings on the stream.
When you stream "INFO-1" you get a 4-byte size (00 00 00 07) followed by the 6 bytes of the string and a zero byte
The bool is streamed as a two byte integer.

The stringstream converts the numeric values to decimal string, so:


#include <cstdint>
#include <iostream>
#include <sstream>

int main(int argc, char **argv) {
std::stringstream str;
str << (uint32_t)0; // "0" in output
str << (uint32_t)0x53486472 // "1397253234" in output i.e. the number in decimal as a string
<< (uint32_t)0 // "0"
<< (uint32_t)1 // "1"
<< (uint32_t)2 // "2"
<< (uint32_t)3 // "3"
<< (uint32_t)4 // "4"
<< (uint32_t)5 // "5"
<< (uint32_t)6 // "6"
<< (uint32_t)7; // "7"

str << (uint32_t)0x54695355 // "1416188757"
<< (char*)"INFO-1"
<< (char*)"869543-1"
<< (char*)"85A-1"
<< (uint16_t)1 // "1"
<< (char*)"C0B3-MV1R-1"
<< (bool)false; // "0"

std::cout << str.str() << std::endl;

return 0;
}

outputs:


01397253234012345671416188757INFO-1869543-185A-11C0B3-MV1R-10

which is exactly what you receive.

You want something closer to this but you need to think about byte order and what QDataStream is expecting to see:


#include <cstdint>
#include <iostream>
#include <sstream>

template <typename T>
std::stringstream& put ( std::stringstream& str, const T& value )
{
union coercion {
T value;
char data[ sizeof ( T ) ];
};

coercion c;
c.value = value;
str.write ( c.data, sizeof ( T ) );
return str;
}

int main(int argc, char **argv) {
std::stringstream str;
put(str, (uint32_t)0);
put(str, (uint32_t)0x53486472);
put(str, (uint32_t)0);
put(str, (uint32_t)1);
put(str, (uint32_t)2);
put(str, (uint32_t)3);
put(str, (uint32_t)4);
put(str, (uint32_t)5);
put(str, (uint32_t)6);
put(str, (uint32_t)7);
put(str, (uint32_t)0x54695355);
str << (char*)"INFO-1"
<< (char*)"869543-1"
<< (char*)"85A-1";
put(str, (uint16_t)1);
str << (char*)"C0B3-MV1R-1";
put(str, false);

return 0;
}