I'm writing a C++ program where I want to read a stream of data from a TCP/IP socket. The data consists of several packets of various lengths and data types, however, it is all received in hex format. The lengths of the packets and their data types can be seen in this image:
.
I want to convert the received hex formatted data back to the packets' respective original data types.
I have found a rather easy way to do this in Python:
- Receive each packet according to their known length
- Encode them as hex
- unpack them according to their data type
Here's a sample:
import socket
import struct
HOST = "192.168.0.25" # The remote host
PORT_30003 = 30003
while (True):
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((HOST, PORT_30003))
print ""
packet_1 = s.recv(4)
packet_2 = s.recv(8)
packet_3 = s.recv(48)
packet_4 = s.recv(48)
packet_5 = s.recv(48)
packet_6 = s.recv(48)
packet_7 = s.recv(48)
packet_8 = s.recv(48)
packet_9 = s.recv(48)
packet_10 = s.recv(48)
packet_11 = s.recv(48)
packet_1 = packet_1.encode("hex")
messageSize = struct.unpack('!i', packet_1.decode('hex'))[0]
print "messageSize = ", messageSize
packet_2 = packet_2.encode("hex")
Time = struct.unpack('!d', packet_2.decode('hex'))[0]
print "Time = ", Time
I don't care a lot about these next packets, so I'll jump to the relevant ones (from packet_12 and onwards). I'm going to split each 48 byte packet into smaller 8 byte size packets, since the 48 bytes is actually 6 separate values as detailed in the picture above...
packet_12x = s.recv(8)
packet_12x = packet_12x.encode("hex")
x = struct.unpack('!d', packet_12x.decode('hex'))[0]
print "X = ", x * 1000
packet_12y = s.recv(8)
packet_12y = packet_12y.encode("hex")
y = struct.unpack('!d', packet_12y.decode('hex'))[0]
print "Y = ", y * 1000
# And so on.....
This produces the message length (which should always be 1108), the server's time and the X, Y position of the robot's tool.
When attempting this in C++ I'm getting a bit confused with the data types, byte ordering, etc. I have managed to produce the first packet correctly (printing 1108 as I expect). However, the next packet does not match the output from the Python implementation. Here's what I have:
connectTcpClient("192.168.0.25");
// This works as expected
int buffer1;
recv(sock, &buffer1, 4, 0);
buffer1 = ntohl(buffer1);
// Prints "messageSize = 1108"
std::cout << "messageSize = " << buffer1 << std::endl;
// This does not produce the same result as the Python code
// When trying with a unsigned long long int instead of double
double buffer2;
recv(sock, &buffer2, 8, 0);
buffer2 = ntohl(buffer2);
// This prints "Time: 0"
std::cout << "Time: " << buffer2 << std::endl;
If I instead try declaring my buffer2
as such:
unsigned long long int buffer2
I get something closer to what I want, however it is of course not a double like I expect. And casting buffer2
to a double
simply results in a 0.
I expect the time to be something like 1379408.432, instead of an integer. The X, Y, and Z coordinates from packet_12 also should be doubles.
The ntohl
function expects a 32 bit integer and changes the value so effectively the bytes are swapped, assuming host byte order is little endian. It will not work on a value of type double
.
You need to read the value into a char
array of size 8, reverse the bytes, then use memcpy
to copy the bytes into a double
.
int i;
unsigned char dbuff[sizeof(double)];
recv(sock, dbuff, sizeof(dbuff), 0);
if (ntohl(0x12345678) == 0x78563412) {
for (i=0; i<sizeof(double)/2; i++) {
unsigned char tmp = dbuff[i];
dbuff[i] = dbuff[sizeof(double)-1-i];
dbuff[sizeof(double)-1-i] = tmp;
}
}
double val;
memcpy(&val, dbuff, sizeof(double));
Note that this is dependent on both the sender and the receiver having the same representation for a double
.