什么是在C中转换字节顺序的可移植方式

时间:2009-12-19 21:20:08

标签: c string network-programming portability endianness

我正在尝试编写可与任何可以建立套接字连接的标准客户端进行通信的服务器(例如telnet客户端)

它最初是一个echo服务器,当然不需要担心网络字节排序。

我熟悉ntohs,ntohl,htons,htonl函数。如果我传输的是16位或32位的整数,或者发送的字符串中的字符是2或4个字节的倍数,那么它们本身就很棒。

我想创建一个对字符串进行操作的函数,例如:

str_ntoh(char* net_str, char* host_str, int len)
{
    uint32_t* netp, hostp;
    netp = (uint32_t*)&net_str;
    for(i=0; i < len/4; i++){
         hostp[i] = ntoh(netp[i]);
    }
}

或类似的东西。上面的假设是单词大小为32位。我们不能确定发送机器上的字数不是16位,还是64位正确?

对于客户端程序,例如telnet,它们必须在发送之前使用hton *,在接收数据后使用ntoh *,是否正确?

编辑:对于那些人而言,因为1-char是一个字节,而字节序无关紧要:

int main(void)
{
    uint32_t a = 0x01020304;
    char* c = (char*)&a;
printf("%x %x %x %x\n", c[0], c[1], c[2], c[3]);

}

运行此代码段。我的输出如下:

$ ./a.out
  4 3 2 1

那些关于powerPC芯片组的人应该得到'1 2 3 4'但我们这些关于intel芯片组的人应该看看我上面得到的东西。

4 个答案:

答案 0 :(得分:17)

也许我在这里遗漏了一些东西,但是你在发送字符串,即字符序列?那你就不用担心字节顺序了。这仅适用于整数中的位模式。字符串中的字符始终采用“正确”的顺序。

修改

Derrick,为了解决您的代码示例,我在Intel i7(little-endian)和旧的Sun Sparc(big-endian)上运行了以下(略微扩展的)程序版本

#include <stdio.h>
#include <stdint.h> 

int main(void)
{
    uint32_t a = 0x01020304;
    char* c = (char*)&a;
    char d[] = { 1, 2, 3, 4 };
    printf("The integer: %x %x %x %x\n", c[0], c[1], c[2], c[3]);
    printf("The string:  %x %x %x %x\n", d[0], d[1], d[2], d[3]);
    return 0;
}

正如你所看到的,我在你的整数打印输出中添加了一个真正的char数组。

小端英特尔i7的输出:

The integer: 4 3 2 1
The string:  1 2 3 4

来自big-endian Sun的输出:

The integer: 1 2 3 4
The string:  1 2 3 4

您的多字节整数确实以不同的字节顺序存储在两台计算机上,但char数组中的字符具有相同的顺序。

答案 1 :(得分:4)

将您的函数签名发布后,您不必担心字节顺序。它接受一个char *,只能处理8位字符。每个字符只有一个字节,就不会出现字节顺序问题。

如果您使用UTF16或UTF32编码发送Unicode,则只会遇到字节顺序问题。并且发送机器的端点与接收机器的端点不匹配。对此的简单解决方案是使用UTF8编码。大多数文本都是通过网络发送的。面向字节,它也没有字节顺序问题。或者您可以发送a BOM.

答案 2 :(得分:2)

如果您希望将它们作为8位编码发送(事实上您使用char意味着这是您想要的),则无需进行字节交换。但是,对于非ASCII字符的无关问题,所以同一个字符> 127在连接的两端显示相同,我建议您发送类似UTF-8的数据,可以表示所有unicode字符,可以安全地视为ASCII字符串。根据默认编码获取UTF-8文本的方式因您使用的平台和库集而异。

如果您正在发送16位或32位编码...您可以在byte order mark中包含一个字符,另一端可以使用该字符来确定字符的字节顺序。或者,您可以假设网络字节顺序,并按照建议使用htons()htonl()。但是,如果您想使用char,请参阅上一段。 : - )

答案 3 :(得分:1)

在我看来,函数原型与其行为不匹配。你传递了一个char *,但是你将它转换为uint32_t *。而且,仔细观察,你是在投射指针的地址,而不是内容,所以我担心你会得到意想不到的结果。也许以下方法会更好:

arr_ntoh(uint32_t* netp, uint32_t* hostp, int len)
  {
  for(i=0; i < len; i++)
    hostp[i] = ntoh(netp[i]);
  }

我基于这样的假设:你真正得到的是一个uint32_t数组,你想在所有这些数组上运行ntoh()。

我希望这有用。