it-swarm.com.de

Htonl () und ntohl () verstehen

Ich versuche, Unix-Sockets zu verwenden, um das Senden einiger UDP-Pakete an localhost zu testen.

Ich verstehe, dass ich beim Festlegen der IP-Adresse und des Ports zum Senden von Paketen meinen sockaddr_in mit Werten füllen würde, die in die Netzwerk-Byte-Reihenfolge konvertiert wurden

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

Drucke

ntohl: 1729232896
htonl: 1729232896
plain: 4711

Daher liefert keine der beiden Funktionen tatsächlich den reinen Wert. Ich hätte erwartet, dass sich die Ergebnisse unterscheiden, da x86 Little-Endian (afaik) ist oder identisch mit der tatsächlichen Zahl 4711 ist. Natürlich verstehe ich nicht, was htonl und ntohl und ihre Varianten tun. Was vermisse ich?

Der relevante Code lautet:

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

Beide Funktionen kehren die Reihenfolge der Bytes um. Warum würde das das Argument selbst zurückgeben?

Versuchen Sie es mit htons(ntohs(4711)) und ntohs(htons(4711)).

5

Wie andere bereits erwähnt haben, kehren sowohl htons als auch ntohs die Bytereihenfolge auf einer Little-Endian-Maschine um und sind auf Big-Endian-Maschinen keine Option.

Was nicht erwähnt wurde ist, dass diese Funktionen einen 16-Bit-Wert annehmen und einen 16-Bit-Wert zurückgeben. Wenn Sie 32-Bit-Werte konvertieren möchten, verwenden Sie stattdessen htonl und ntohl.

Die Namen dieser Funktionen stammen von den traditionellen Größen bestimmter Datentypen. s steht für short, während l für long steht. Ein short ist normalerweise 16-Bit, während auf älteren Systemen long 32-Bit war. 

In Ihrem Code müssen Sie htonl nicht für rec_addr aufrufen, da dieser Wert von inet_addr zurückgegeben wurde und diese Funktion die Adresse in der Netzwerk-Byte-Reihenfolge zurückgibt.

Sie müssen jedoch htons unter rec_port aufrufen.

7
dbush

"Bytereihenfolge im Netzwerk" bedeutet immer Big Endian.

Die Reihenfolge der Host-Bytes hängt von der Architektur des Hosts ab. Je nach CPU kann die Reihenfolge der Host-Bytes Little Endian, Big Endian oder etwas anderes sein. (g) libc passt sich an die Host-Architektur an.

Da die Intel-Architektur wenig endian ist, bedeutet dies, dass beide Funktionen dasselbe tun: Byte-Reihenfolge umkehren.

4
gudok

diese Funktionen sind schlecht benannt. Host to network und network to Host sind eigentlich das Gleiche und sollten eigentlich als 'Endianness ändern' bezeichnet werden, wenn dies eine kleine Endian-Maschine ist.

Also auf einem kleinen Endian-Rechner

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

und senden Sie die Nummer auf dem Draht. Und wenn Sie eine große Endian-Nummer von der Leitung bekommen

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

auf einer großen Maschine 

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

und

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

und in den letzten Fällen sehen Sie, dass diese Funktionen nichts bewirken

2
pm100