Understanding htonl() and ntohl()

强颜欢笑 提交于 2019-11-28 03:38:21

问题


I am trying to use unix sockets to test sending some udp packets to localhost.

It is my understanding that when setting ip address and port in order to send packets, I would fill my sockaddr_inwith values converted to network-byte order. I am on OSX and I'm astonished that this

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

Prints

ntohl: 1729232896
htonl: 1729232896
plain: 4711

So neither function actually returns the plain value. I would have expected to see either the results differ, as x86 is little-endian (afaik), or be identical and the same as the actual number 4711. Clearly I do not understand what htonl and ntohl and their variants do. What am I missing?

The relevant code is this:

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;
}

回答1:


Both functions reverse the bytes' order. Why would that return the argument itself?

Try htons(ntohs(4711)) and ntohs(htons(4711)).




回答2:


As others have mentioned, both htons and ntohs reverse the byte order on a little-endian machine, and are no-ops on big-endian machines.

What wasn't mentioned is that these functions take a 16-bit value and return a 16-bit value. If you want to convert 32-bit values, you want to use htonl and ntohl instead.

The names of these functions come from the traditional sizes of certain datatypes. The s stands for short while the l stands for long. A short is typically 16-bit while on older systems long was 32-bit.

In your code, you don't need to call htonl on rec_addr, because that value was returned by inet_addr, and that function returns the address in network byte order.

You do however need to call htons on rec_port.




回答3:


"Network byte order" always means big endian.

"Host byte order" depends on architecture of host. Depending on CPU, host byte order may be little endian, big endian or something else. (g)libc adapts to host architecture.

Because Intel architecture is little endian, this means that both functions are doing the same: reversing byte order.




回答4:


these functions are poorly named. Host to network and network to host are actually the same thing and really should be called 'change endianness if this is a little endian machine'

So on a little endian machine you do

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

and send the be number on the wire. And when you get a big endian number from the wire

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

on a big end machine

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

and

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

and in the last cases you see that these functions do nothing



来源:https://stackoverflow.com/questions/36924598/understanding-htonl-and-ntohl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!