【问题标题】:What's a portable way of converting Byte-Order of strings in C什么是在 C 中转换字符串字节顺序的可移植方式
【发布时间】:2009-12-19 21:20:08
【问题描述】:

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

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

我熟悉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]);

}

运行这个 sn-p 代码。我的输出如下:

$ ./a.out
  4 3 2 1

那些使用 powerPC 芯片组的人应该得到“1 2 3 4”,但我们这些使用 intel 芯片组的人应该看到我上面得到的大部分内容。

【问题讨论】:

  • 我认为这取决于每个字符的大小。如果每个字符只使用一个字节,则无需担心。
  • 如果您将 uint32_t* 转换为 char* 那么是的,顺序很重要。 “我们认为字节顺序对 char* 无关紧要”的意思是,如果你声明一个字符数组,并且只使用字符而不使用更大的类型,你不必交换,它只是多字节整数这很重要(uint32_t 是一个多字节整数,所以这就是您的示例的行为方式)。
  • 您的示例代码不使用字符串,它使用uint32_t 的内存表示。有区别。如果您改为使用char *c = "foo";,则输出为 66、6f、6f、0。无论机器的字节序如何。我还没有编写回显客户端/服务器,但我已经用 C 编写了 HTTP 客户端和服务器。您不需要对字符串进行字节交换。
  • @Derrick:别再侮辱了。相反,你为什么不试试呢? char foo[] = "你好,世界";写(fd,foo,sizeof(foo)); -- 在 Intel 机器上执行 write() 并在 PowerPC 机器上执行 read() 并见证它是如何工作的。否则我建议你重新阅读这里的一些 cmets。
  • @Derrick:示例服务器:stackoverflow.com。它返回字符串,不同的客户端不需要做任何特殊的事情来检测服务器的字节顺序。

标签: c string network-programming portability endianness


【解决方案1】:

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

编辑:

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

#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

以及大端Sun的输出:

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

你的多字节整数确实在两台机器上以不同的字节顺序存储,但是char数组中的字符顺序相同。

【讨论】:

  • 字符串是字符序列是的。通过网络在具有相同字节序的两台计算机之间发送此数据无关紧要。但是,如果您没有进行任何字节顺序转换,并且做了类似 char* str = "abcd";并将其发送到小端机器上,然后在大端机器上接收,当您寻址 str[0] 时,它将是 d,而不是 a。 stackoverflow.com/questions/526030/…
  • @Derrick:不,那是错误的。对于字符串,第一个字符将始终位于第一个位置,依此类推。它不像多字节整数。
  • @Derrick:为了说明 Thomas 的观点……假设您有一个整数数组,{ 0xaabb, 0xccdd }。将其转换为不同的字节顺序,整数中的字节顺序会变形为 0xbbaa、0xddcc。但是数组中整数的顺序不会改变。所以它是 { 0xbbaa, 0xddcc } 而不是 { 0xddcc, 0xbbaa }。现在想象这些是 8 位整数而不是 16。如果您有一个数组 {0xaa, 0xbb, 0xcc, 0xdd},在数组元素 (0xaa) 内没有要交换的字节,它是一个字节。而且您不会交换单个字节,因为这会改变数组的顺序。
  • 真的吗?运行这个: 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]);当我运行它时,我得到 4 3 2 1
  • @Derek - 这是一个 32 位整数,而不是字节数组。如果您声明: char a[] = {1,2,3,4};它将始终保持相同的顺序。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    如果您想将它们作为 8 位编码发送(您使用 char 的事实表明这是您想要的),则无需进行字节交换。但是,对于非ASCII字符的无关问题,使相同的字符&gt; 127在连接的两端出现相同的情况,我建议您以UTF-8之类的方式发送数据,它可以代表所有unicode字符,并且可以安全地视为 ASCII 字符串。基于默认编码获取 UTF-8 文本的方式因平台和您使用的库集而异。

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

    【讨论】:

    • 应该注意UTF-8不受字节顺序的影响。 UTF-16 和 UTF-32 是。
    【解决方案4】:

    在我看来,函数原型与其行为不符。您传入了一个 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()。

    我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-07-21
      • 1970-01-01
      相关资源
      最近更新 更多