web-dev-qa-db-ja.com

htonl()およびntohl()を理解する

UNIXソケットを使用して、いくつかのudpパケットをローカルホストに送信するテストをしようとしています。

パケットを送信するためにIPアドレスとポートを設定する場合、sockaddr_inwith値はネットワークバイトオーダーに変換されます。私は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はリトルエンディアン(afaik)であるため、結果が異なるか、実際の番号4711と同じで同じであることが予想されます。明らかに、htonlおよびntohlとそのバリアントはそうです。私は何が欠けていますか?

関連するコードは次のとおりです。

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

両方の関数は、バイトの順序を逆にします。なぜそれが引数自体を返すのでしょうか?

htons(ntohs(4711))ntohs(htons(4711))を試してください。

5

他の人が述べたように、htonsntohsは両方とも、リトルエンディアンのマシンではバイト順を逆にし、ビッグエンディアンのマシンではノーオペレーションです。

言及されなかったのは、これらの関数が16ビット値を取り、16ビット値を返すということです。 32ビット値を変換する場合は、代わりにhtonlntohlを使用します。

これらの関数の名前は、特定のデータ型の従来のサイズに由来します。 sshortを表し、llongを表します。 shortは通常16ビットですが、古いシステムではlongは32ビットでした。

コードでは、rec_addrによってhtonlを呼び出す必要はありません。その値はinet_addrによって返され、その関数はネットワークバイト順でアドレスを返すためです。

ただし、rec_porthtonsを呼び出す必要があります。

8
dbush

「ネットワークバイトオーダー」は常にビッグエンディアンを意味します。

「ホストのバイト順」は、ホストのアーキテクチャに依存します。 CPUに応じて、ホストのバイト順はリトルエンディアン、ビッグエンディアン、またはその他の場合があります。 (g)libcはホストアーキテクチャに適応します。

Intelアーキテクチャはリトルエンディアンであるため、これは両方の関数が同じことを実行していることを意味します。つまり、バイト順を逆にします。

5
gudok

これらの関数の名前は不十分です。 Host to networkおよびnetwork 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)

最後のケースでは、これらの関数は何もしないことがわかります

3
pm100