1. 大端模式:数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中。   
  2. 小端模式:数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中。
  3. 所以只有同时拥有多个字节构成的才有大小端概念,比如int、short、long等同时多个字节的类型,而char,或者字符串是没有这个概念的
  4. 换个说法就是大小端决定了多字节数据在系统中的读取、存储顺序,若实际的数据存储(如从报文中获取的数据)与当前系统的顺序不一致,必然将导致错误
  5. 若自己处理大小端问题时,一定不能以默认的指针偏移来进行处理(比如解析IP,如下示例),除非引入了一些大小端的编译宏做了控制,否则就以取值的方式来进行操作,因为如何取值就是系统自身决定的
  1. /* 主机序IP打印(取值计算,无关大小端),以 %d.%d.%d.%d的形式 */
  2. #define BYOD_IPV4_STR(HOST_IPV4) \
  3. (HOST_IPV4 >> 24) & 0xFF, \
  4. (HOST_IPV4 >> 16) & 0xFF, \
  5. (HOST_IPV4 >> 8) & 0xFF, \
  6. HOST_IPV4 & 0xFF

image.jpeg

在进行网络通信时是否需要进行字节序转换?

相同字节序的平台在进行网络通信时可以不进行字节序转换,但是跨平台进行网络数据通信时必须进行字节序转换。

原因如下:网络协议规定接收到得第一个字节是高字节,存放到低地址,所以发送时会首先去低地址取数据的高字节。

小端模式的多字节数据在存放时,低地址存放的是低字节,而被发送方网络协议函数发送时会首先去低地址取数据(想要取高字节,真正取得是低字节),接收方网络协议函数接收时会将接收到的第一个字节存放到低地址(想要接收高字节,真正接收的是低字节),所以最后双方都正确的收发了数据。

而相同平台进行通信时,如果双方都进行转换最后虽然能够正确收发数据,但是所做的转换是没有意义的,造成资源的浪费。
而不同平台进行通信时必须进行转换,不转换会造成错误的收发数据,字节序转换函数会根据当前平台的存储模式做出相应正确的转换,如果当前平台是大端,则直接返回不进行转换,如果当前平台是小端,会将接收到得网络字节序进行转换。

编码时正确的处理流程**

  1. 首先仅有多字节的数据才有大小端的概念
  2. 从网络数据中接受处理时,必须调用转换接口(如 ntohl 把unsigned long类型从网络序转换到主机序 ),转换为主机序再处理
  3. 有数据需要发送到网络中时,必须调用转换接口(如 htonl 把unsigned long类型从主机序转换到网络序),转换为网络序再发送
  4. ntohl 或 htonl 等转换接口,会自动判断是否需要进行大小端的转换**

    转换接口

    网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。
    为了进行转换 bsd socket提供了转换的函数 有下面四个:

  5. 16位数据

    1. ntohs 把unsigned short类型从网络序转换到主机序
    2. htons 把unsigned short类型从主机序转换到网络序
  6. 32位数据
    1. ntohl 把unsigned long类型从网络序转换到主机序
    2. htonl 把unsigned long类型从主机序转换到网络序

      大小端转换

      ```c //先与再移位,按原理模拟位移,一般用这种 (( a&0x000000ff)<< 24 ) | (( a&0x0000ff00 ) << 8 )
      | (( a&0x00ff0000 ) >> 8 )
      | (( a&0xff000000 ) >> 24 )

//也可以直接操作指针赋值 int IntChangeBytes(int value) { int tmp_value; uint8_t index_1, index_2;

  1. index_1 = (uint8_t *)&tmp_value;
  2. index_2 = (uint8_t *)&value;
  3. *index_1 = *(index_2+3);
  4. *(index_1+1) = *(index_2+2);
  5. *(index_1+2) = *(index_2+1);
  6. *(index_1+3) = *index_2;
  7. return tmp_value;

} ```