编码解码字节

4
我有一个嵌入式设备,它以这种格式(日期为4个字节)向我发送UTC日期:
 buffer.push_back((BYTE)(time_utc & 0x000000FF));
 buffer.push_back((BYTE)((time_utc & 0x0000FF00) >> 8));
 buffer.push_back((BYTE)((time_utc & 0x00FF0000) >> 16));
 buffer.push_back((BYTE)((time_utc & 0xFF000000) >> 24));

在服务器上,我正在接收字节并将它们存储在 `socket_buf` 中,从索引0-3开始,并使用以下逻辑进行解码。
mypkt.dateTime = ( ( socket_buf[0] << 24) +  
(socket_buf[1 ] << 16) +  socket_buf[2] << 8) + 
(socket_buf[3] << 0));

但是我不确定我是否正确地解码了它,因为我得到的日期不正确。有人能建议我正确的解码方式吗?我正在使用Linux命令解码日期(16711840是我的解码数字)。
#date -d @16711840

这两台机器的字节序一致吗? - Hassan Syed
我认为这与网络中的字节顺序有关。网络字节顺序是大端字节序,而大多数英特尔处理器都是小端字节序=> 这可能是错误出现的地方。您是否查看了C函数htons()ntohs() - jcxz
但是我还发送了其他数据,我将正确提取它们,我更关心的是我的逻辑是否正确地从socket_buf解码。我不确定是否需要像设备上那样进行&-ing操作。 - Rohit
请展示 socket_buf 的声明,它可能是错误的原因。 - Lundin
socket_buf被声明为无符号字符指针。 - Rohit
在服务器端,您正在错误地进行转换。应该是:(sock_buf[3] << 24) + (sock_buf[2] << 16) + (sock_buff[1] << 8) + sock_buf[0]; - ja_mesa
3个回答

8

代码编写是 小端 的 - 它首先发送最不重要的字节。

您的读取代码期望 大端 - 它将零号字节向左移动 24 位。

请注意,无论如何,这些代码都不依赖于本地计算机的字节顺序 - 编写的代码与此无关,只是它们彼此不一致。

请尝试使用以下内容:

mypkt.dateTime = ((socket_buf[0] <<  0) +
                  (socket_buf[1] <<  8) + 
                  (socket_buf[2] << 16) +
                  ((uint32_t)socket_buf[3] << 24));

在最后一个移位操作时,强制转换是必要的,因为0x80 - 0xff将被转换为signed int,并且不确定会发生什么事情使得这些位移动到符号位(感谢@Lundin)。

注意:无论你使用哪种字节序来表示它,16711840都不是“当前”的Unix纪元日期时间值。你可能在其他地方遇到了问题。


0

由于socket_buf被声明为无符号字符,所以socket_buf[0] << 24是一个错误。

socket_buf[0]是无符号字符,并将被提升为int。在这个特定系统上,它是16位还是32位并不重要,因为程序最终都会崩溃。在两种情况下,它最终都成为了有符号变量。您将左移一个有符号变量,并将二进制1移入符号位。然后您对此进行加法运算。

编写解码的正确方法如下:

mypkt.dateTime = ( ((uint32_t)socket_buf[0] << 24) |
                   ((uint32_t)socket_buf[1] << 16) |
                   ((uint32_t)socket_buf[2] <<  8) |
                   ((uint32_t)socket_buf[3] <<  0) );

此外,您似乎在编码和解码之间反向更改了字节顺序。我不太明白这与字节序有什么关系,您的代码只是在buffersock_buf之间使用了不一致的顺序。

一个 unsigned char 肯定会被提升为 _unsigned_ int,但无论哪种情况,它都不会导致崩溃。 - Alnitak
好的,澄清一下 - 由于0-0xff都适合于一个int中,它确实会被提升为int而不是uint,但是_这不会导致符号扩展_。结果的int仍将具有范围0-0xff - Alnitak
1
@Alnitak 它将被提升为 int,而不是 unsigned int (C11 6.3.1.1)。如果 int 是32位,则会得到以下代码: int32_t my_int=0xFF; my_int << 24; 这是未定义的行为,按照 C11 6.5.7。 - Lundin
正确 - 最高位可能会丢失(取决于实现方式)。 - Alnitak

-3

你清空了socket_buf吗?

你确定你的计算机是大端字节序吗?

此外,我给你一个建议:使用或运算符代替加号。

这可以节省更多时间。

mypkt.dateTime = (long) ( ( socket_buf[0] << 24) |  (socket_buf[1] << 16) |  socket_buf[2] << 8) | (socket_buf[3] << 0));

1
原帖中的代码是独立于字节序的。按位或运算等同于加法。启用优化器后不应有性能差异(但在这种情况下,按位或可能更易于阅读和理解)。此外,如果“socket_buf”是错误的整数类型,则您提出的方法将无效。而将其转换为有符号长整型是毫无意义和不合理的。 - Lundin
是的,套接字缓冲区已经清空了。Lundin,你的意思是说我不需要像设备原始代码中那样进行&-ing操作吗? - Rohit
这段代码对我来说似乎运行正常:mypkt.dateTime = ((socket_buf[HB_PKT_DATE_TIME] << 0) + (socket_buf[HB_PKT_DATE_TIME+1] << 8) + (socket_buf[HB_PKT_DATE_TIME+2] << 16) + (socket_buf[HB_PKT_DATE_TIME+3] << 24)); - Rohit
@Rohit 是的,那与我回答中的代码等效 - 这个答案仍然将字节序解码错误。 - Alnitak
@Rohit 在没有超过0x7F的字节值时,它可能完美地工作。 - Lundin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接