c++ - 如何正确转换也是不同数据类型的各种(已知)长度数据包的流?

标签 c++ tcp

问题:

我正在编写一个 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,而不是整数。来自 packet_12 的 X、Y 和 Z 坐标也应该是 double 值。

最佳答案

ntohl 函数需要一个 32 位整数并更改值以便有效地交换字节,假设主机字节顺序是小端。它不适用于 double 类型的值。

您需要将值读入大小为 8 的 char 数组,反转字节,然后使用 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 具有相同的表示。

关于c++ - 如何正确转换也是不同数据类型的各种(已知)长度数据包的流?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55497366/

相关文章:

c++ - 如何创建 ELF 可执行文件?

silverlight - 最小化丢失数据包对通过 TCP 发送的实时媒体流的影响的最佳方法是什么?

ssl - 如何解决 "NSS error -12276 (SSL_ERROR_BAD_CERT_DOMAIN)"

c# - 帮助事件驱动的 TCP 服务器

python-2.7 - scapy 中的 <Raw> 标签,如何解码内容?

c++ - 2016 年 MSVC 不支持 noexcept 关键字

c++ - 采用指向基类的指针的函数是否也接受从基类私有(private)派生的子类?

c++ - 将 .o 文件转换为 .exe

python - 在 python 中具有事件 TCP 连接的 HTTP 服务器

c++ - boost_multi 数组太大? bad_alloc 错误