【问题标题】:Understanding htonl() and ntohl()理解 htonl() 和 ntohl()
【发布时间】:2016-08-23 19:17:38
【问题描述】:

我正在尝试使用 unix 套接字来测试向 localhost 发送一些 udp 数据包。

据我了解,在设置 ip 地址和端口以发送数据包时,我将使用转换为网络字节顺序的值填充我的 sockaddr_in。 我在 OSX 上,对此我感到很惊讶

printf("ntohl: %d\n", ntohl(4711));
printf("htonl: %d\n", htonl(4711));
printf("plain: %d\n", 4711);

打印

ntohl: 1729232896
htonl: 1729232896
plain: 4711

所以这两个函数实际上都没有返回纯值。我本来希望看到结果会有所不同,因为 x86 是 little-endian (afaik),或者与实际数字 4711 相同且相同。显然我不明白 htonlntohl 及其变体的作用.我错过了什么?

相关代码是这样的:

int main(int argc, char *argv[])
{
   if (argc != 4)
   {
      fprintf(stderr, "%s\n", HELP);
      exit(-1);
   }

   in_addr_t rec_addr = inet_addr(argv[1]); // first arg is '127.0.0.1'
   in_port_t rec_port = atoi(argv[2]);      // second arg is port number
   printf("Address is %s\nPort is %d\n", argv[1], rec_port);
   char* inpath = argv[3];

   char* file_buf;
   unsigned long file_size = readFile(inpath, &file_buf); // I am trying to send a file
   if (file_size > 0)
   {
      struct sockaddr_in dest;
      dest.sin_family      = AF_INET;
      dest.sin_addr.s_addr = rec_addr; // here I would use htons
      dest.sin_port        = rec_port;
      printf("ntohs: %d\n", ntohl(4711));
      printf("htons: %d\n", htonl(4711));
      printf("plain: %d\n", 4711);
      int socket_fd = socket(AF_INET, SOCK_DGRAM, 0);
      if (socket_fd != -1)
      {
         int error;
         error = sendto(socket_fd, file_buf, file_size + 1, 0, (struct sockaddr*)&dest, sizeof(dest));
         if (error == -1)
            fprintf(stderr, "%s\n", strerror(errno));
         else printf("Sent %d bytes.\n", error);
      }
   }

   free(file_buf);
   return 0;
}

【问题讨论】:

  • 请注意,您的文字显示“htons”和“ntohs”,但实际上您调用的是htonl()ntohl()
  • @JohnBollinger 是的,这来自于尝试两者的结果相同,感谢您的评论。

标签: c sockets networking endianness


【解决方案1】:

正如其他人所提到的,htonsntohs 在 little-endian 机器上反转字节顺序,在 big-endian 机器上是 no-ops。

没有提到的是这些函数采用 16 位值并返回 16 位值。如果要转换 32 位值,请改用 htonlntohl

这些函数的名称来自某些数据类型的传统大小。 s 代表short,而l 代表longshort 通常是 16 位,而在旧系统上 long 是 32 位。

在您的代码中,您无需在rec_addr 上调用htonl,因为该值由inet_addr 返回,并且该函数以网络字节顺序返回地址。

但是,您需要在rec_port 上致电htons

【讨论】:

  • 如果我尝试在 localhost 上运行的两个程序之间发送数据包,并将 rec_porthtons 处理,则 8080 变为 36895。
【解决方案2】:

“网络字节序”总是指大端。

“主机字节顺序”取决于主机的架构。根据 CPU,主机字节顺序可能是小端、大端或其他。 (g)libc 适应主机架构。

因为英特尔架构是小端序,这意味着两个函数都在做同样的事情:反转字节顺序。

【讨论】:

【解决方案3】:

这两个函数都颠倒了字节的顺序(在 little-endian 机器上)。为什么会返回参数本身?

试试htons(ntohs(4711))ntohs(htons(4711))

【讨论】:

  • 嗯……对。我想真正的问题是为什么我无法向 localhost 发送任何内容,除非我使用纯 IP 地址,但这是一个单独的问题。
  • "reverse the bytes" 如果它们必须在您的系统上反转...您可以猜测它们是否都反转了字节...取决于您的拱门。
【解决方案4】:

这些函数命名不当。 Host to networknetwork to host 实际上是同一个东西,真的应该叫做'如果这是一个小端机器,就改变字节顺序'

所以你在一个小端机器上做

net, ie be, number = htonl / ntohl (le number)

并通过网络发送 be 号码。当你从电线中得到一个大端序号时

le num = htonl/ntohl (net ,ie be, number)

在大端机器上

net, ie be, number = htonl / ntohl (be number)

 be num = htonl/ntohl (net ,ie be, number)

在最后一种情况下,您会看到这些函数什么都不做

【讨论】:

  • 好吧,小心点。虽然现在很少有机器使用不同于小端和大端的字节顺序,但过去也有过这样的机器,而且有一定的知名度。至少可以想象,现在或未来的机器,例如,htonl() 不是它自己的逆。 POSIX 将这些函数设计为能够在需要时处理这些问题。
  • 好的,那么它应该被称为'如果这不是一个大端机器,就把字节序改为大'
  • 即使这两个函数做同样的事情,为它们使用不同的名称仍然很有用,因为它阐明了代码的意图——即如果我看到 x=ntohl(blah) 我知道分配的值to x 将在语义上有意义,而如果我看到 x=htonl(blah) 我知道 x 的值不是我可以依赖的原生字节序。如果代码只是 x=maybe_endian_swap(blah),那么我是否可以例如printf("%i\n", x);然后查看打印的语义上有意义的值。
  • 字节序不是起点,而是终点:commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
猜你喜欢
  • 2019-07-23
  • 2013-03-29
  • 2014-07-20
  • 1970-01-01
  • 1970-01-01
  • 2012-07-10
  • 1970-01-01
  • 1970-01-01
  • 2019-01-12
相关资源
最近更新 更多