Как правильно преобразовать поток пакетов различной (известной) длины, которые также имеют разные типы данных? - PullRequest
1 голос
/ 03 апреля 2019

Проблема:

Я пишу программу на C ++, где хочу прочитать поток данных из сокета TCP / IP.Данные состоят из нескольких пакетов различной длины и типов данных, однако все они принимаются в шестнадцатеричном формате.Длины пакетов и их типы данных можно увидеть на этом изображении: table.Я хочу преобразовать полученные данные в шестнадцатеричном формате обратно в соответствующие исходные типы данных пакетов.

Фон и то, что я пробовал:

Я нашел довольно простой способ сделать это вPython:
- получать каждый пакет в соответствии с их известной длиной
- кодировать их как шестнадцатеричные
- распаковывать их в соответствии с их типом данных

Вот пример:

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

Меня не волнуют следующие пакеты, поэтому я перейду к соответствующим (начиная с packet_12 и далее).Я собираюсь разбить каждый 48-байтовый пакет на более мелкие 8-байтовые пакеты, так как 48 байтов - это на самом деле 6 отдельных значений, как показано на рисунке выше ...

 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.....

Это приводит к длине сообщения (который всегда должен быть 1108), время сервера и положение X, Y инструмента робота.


Попытка C ++:

При попытке сделать это в C ++ я немного путаюсь с типами данных, порядком байтов и т. Д. Мне удалось правильно сформировать первый пакет.(печать 1108 как я и ожидал).Однако следующий пакет не совпадает с выводом из реализации Python.Вот что у меня есть:

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; 

Если я вместо этого попытаюсь объявить свой buffer2 как таковой: unsigned long long int buffer2
Я получу что-то ближе к тому, что я хочу, однако это, конечно, не двойникЯ жду.А приведение buffer2 к double просто приводит к 0.

Ожидаемый результат

Я ожидаю, что время будет примерно 1379408.432, а не целым числом.Координаты X, Y и Z из пакета_12 также должны быть двойными.

1 Ответ

1 голос
/ 03 апреля 2019

Функция ntohl ожидает 32-разрядное целое число и изменяет значение настолько эффективно, что байты меняются местами, предполагая, что порядок байтов хоста имеет младший порядок. Он не будет работать со значением типа double.

Вам необходимо прочитать значение в массив char размера 8, обратить байты в обратном направлении, а затем использовать memcpy, чтобы скопировать байты в 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));

Обратите внимание, что это зависит от того, как отправитель и получатель имеют одинаковое представление для double.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...