网络编程--网络字节序和主机字节序详解

转自:https://blog.****.net/msdnwolaile/article/details/50727653

空中传输的字节的顺序即网络字节序为标准顺序,考虑到与协议的一致以及与同类其它平台产品的互通,在程序中发数据包时,将主机字节序转换为网络字节序,收数据包处将网络字 节序转换为主机字节序。网络程序开发时 或是跨平台开发时 应该注意保证只用一种字节序 不然两方的解释不一样就会产生bug。

我们都知道,如今的通讯方式已经趋向与多样化,异构通信(计算机软件(操作系统) + 计算机硬件(内核架构,ARM,x86)不同)也已经很普遍了,如,手机和电脑中的qq进行通信,,数据在传输的过程中,一定有一个标准化的过程,也就是说:从主机a到主机b进行通信:

a的固有数据存储-------标准化--------转化成b的固有格式

如上而言:a或者b的固有数据存储格式就是自己的主机字节序,上面的标准化就是网络字节序(也就是大端字节序)

a的主机字节序----------网络字节序 ---------b的主机字节序

主机字节序
就是自己的主机内部,内存中数据的处理方式,可以分为两种:
大端字节序(big-endian):按照内存的增长方向,高位数据存储于低位内存中(最直观的字节序 )
小端字节序(little-endian):按照内存的增长方向,高位数据存储于低位内存中(符合人的思维的字节序)

网络字节序

内存中的多字节数据都有大小端之分,磁盘文件中的多字节数据相对于文件中的偏移地址也有大小端之分,同样,网络数据流也有大小端之分。
网络数据流的地址规定:先发出的数据是低地址,后发出的数据是高地址。发送主机通常将发送缓冲区中的数据按内存地址从低到高的顺序发出,为了不使数据流乱序,接收主机也会把从网络上接收的数据按内存地址从低到高的顺序保存在接收缓冲区中。
TCP/IP协议规定:网络数据流应采用大端字节序,即低地址高字节。

由于两端的两个主机的大小端不一定相同,因此为了使这些网络数据具有更强的可移植性,使相同的代码在大端和小端主机上都能正常运行,我们可以调用以下库函数进行网络字节序和主机字节序的相关转换:

#include<arpa/inet.h>
//将主机字节序转换为网络字节序
uint32_t htonl(uint32_t hostlong);           //将32长整数从主机字节序转换为网络字节序
uint16_t htons(uint16_t hostshort); 

//将网络字节序转换为主机字节序
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
// h表示主机(host),n表示网络(net),l表示32位长整数,s表示16位短整数。

常用的转换函数
htons ntohs htonl ntohl (s 就是short l是long h是host n是network)

网络编程--网络字节序和主机字节序详解

如何判断我们的主机是那一种的呢???这个我们可以通过程序来进行验证:
方法一:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 
  4 int main()
  5 {
  6         union
  7         {
  8                 short s;
  9                 char c[sizeof(short)];
 10         }un;
 11         un.s = 0x0102;
 12         if(sizeof(short)==2)
 13         {
 14                 if(un.c[0] == 1 && un.c[1] == 2)
 15                         printf("Big-Endian\n");
 16                 else if(un.c[0] == 2 && un.c[1] == 1)
 17                         printf("Little-Endian\n");
 18                 else
 19                         printf("Unknown\n");
 20         }
 21         else
 22                 print("sizeof(short)=%d\n",sizeof(short));
 23         exit(0);
 24 }

编译 && 运行 ----- Linux(LE):

$ uname -a
Linux localhost.localdomain 2.6.32-642.el6.x86_64 #1 SMP Tue May 10 17:27:01 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
$ gcc -o byte-order byte-order.c
$ ./byte-order 
Little-Endian

方法二:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>

int main()
{
    uint16_t HostByteOrder = 0x0102;
    uint16_t NetByteOrder = htons(HostByteOrder);
    if (HostByteOrder == NetByteOrder)
        printf("Big Endian\n");
    else
        printf("Little Endian\n");
    return 0;
}

原理是:
我虽然不知道主机字节序是啥,但是我可以通过htons这样的函数,将其统一转换为网络字节序。
网络字节序是大端(BE)。
我拿转换前的数值和转换后的大端数值进行比较:
如果相等,说明当前主机字节序等价于网络字节序,BE;否则,当前主机字节序为LE。

需要注意:
构建主机字节序的数值时,不要使在LE和BE下都一样的数值,例如:
(16BIT):
0XFFFFFFFF
0XAABBBBAA
这样的数值,在LE和BE下数值都是相等的,=.=!!!

参考:https://www.cnblogs.com/zzy-frisrtblog/p/5839545.html