UN5L9——大小端模式

本章代码关键字

1
2
3
4
BitConverter.IsLittleEndian            //判断当前是否为小端模式
IPAddress.HostToNetworkOrder() //转换为网络字节序,相当于就是转为大端模式
IPAddress.NetworkToHostOrder() //网络字节序转为本机字节序,本机字节序为小端模式,就转换为小端模式
Array.Reverse() //数组颠倒顺序,可用于从大小端模式与本机不一致的远端接收到的字节数组进行转换

大小端模式

  • 大端模式

    是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中
    这样的存储模式有点儿类似于把数据当作字符串顺序处理,地址由小向大增加,数据从高位往低位放
    它更符合人类的阅读习惯

  • 小端模式

    是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中

何为高低字节:
我们阅读数字时,一般是从左往右看,看到数字的位的顺序就是从高到低,例如:127
127​的百位是高位,在左边,而个位是低位,在右边,我们阅读二进制的数0111 1111​也不例外,
因此,数字最左边的为就是高字节,最右边的位就是低字节

何为高低地址:内存开头一边的地址是低地址,末尾一边的地址是高地址

举例说明:十六进制数据:0x11223344

  • 大端模式存储

    从低地址向高地址看去(从内存首看到尾),数字的高位在头,数字的低位在尾,这样的顺序符合人类阅读习惯

    1
    2
    3
    4
    高字节 ——> 低字节
    11 22 33 44
    0 1 2 3
    低地址 ——> 高地址
  • 小端模式存储

    从低地址向高地址看去(从内存首看到尾),数字的低位在头,数字的高位在尾,它的顺序与人类阅读习惯相反

    1
    2
    3
    4
    低字节 <—— 高字节
    44 33 22 11
    0 1 2 3
    低地址 ——> 高地址

大小端模式会根据主机硬件环境不同、语言不同而有所区别,当我们前后端是不同语言开发且运行在不同主机上时
前后端需要对大小端字节序定下统一的规则

一般让前端迎合后端,因为字节序的转换也是会带来些许性能损耗的,网络游戏中要尽量减轻后端的负担, 一般情况下:

  • C# 和 Java/Erlang/AS3 通讯需要进行大小端转换,前端C#从小变大
  • C# 与 C++通信不需要特殊处理

我们不用死记硬背和谁通讯要注意大小端模式,当开发时,发现后端收到的消息和前端发的不一样
在协议统一的情况下,往往就是因为大小端造成的,这时我们再转换模式即可

注意:Protobuf已经帮助我们解决了大小端问题,即使前后端语言不统一,使用它也不用过多考虑字节序转换的问题

为什么有大小端模式

大小端模式其实是计算机硬件的两种存储数据的方式,我们也可以称大小端模式为 大小端字节序

对于我们来说,大端字节序阅读起来更加方便,为什么还要有小端字节序呢?
原因是,计算机电路先处理低位字节,效率比较高

计算机处理字节序的时候,不知道什么是高位字节,什么是低位字节
它只知道按顺序读取字节,先读第一个字节,再读第二个字节
如果是大端字节序,先读到的就是高位字节,后读到的就是低位字节,小端字节序正好相反

因为计算机都是从低位开始读取字节的,所以,计算机的内部处理都是小端字节序

但是,我们人类的读写习惯还是大端字节序
所以,除了计算机的内部处理,其它场合几乎都是大端字节序,比如网络传输和文件存储

一般情况下,操作系统都是小端模式而通讯协议都是大端模式,但是具体的模式,还是要根据硬件平台,开发语言来决定
主机不同,开发语言不同,可能采用的大小端模式也会不一致

大小端模式对于我们的影响

我们记住一句话:只有读取的时候,才必须区分大小端字节序,其它情况都不用考虑

因此对于我们来说,在网络传输当中我们传输的是字节数组,那么我们在收到字节数组进行解析时,就需要考虑大小端的问题
虽然TCP/IP协议规定了在网络上必须采用网络字节顺序(大端模式),但是具体传输时采用哪种模式,都是根据前后端语言、设备决定的

在进行网络通讯时,前后端语言不同时,可能会造成大小端不统一,一般情况下:

  • C# 和 Java/Erlang/AS3 通讯需要进行大小端转换,因为C#是小端模式 Java/Erlang/AS3是大端模式
  • C# 与 C++通信不需要特殊处理 他们都是小端模式

大小端转换

  1. 判断是大小端哪种模式

    取决于你的主机硬件环境、语言

    1
    print("是否是小端模式:" + BitConverter.IsLittleEndian);

    image

  2. 简单的转换API 只支持几种类型

    转换为网络字节序,相当于就是转为大端模式,以下的方法只支持short​,int​,long

    1. 本机字节序转为网络字节序

      它会输出字节顺序颠倒的int​数(小端模式下),需要再自行转换为字节数组

      1
      2
      int i = 99;
      byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(i));
    2. 网络字节序转为本机字节序

      1
      2
      int receI = BitConverter.ToInt32(bytes, 0);
      receI = IPAddress.NetworkToHostOrder(i);
  3. 通用的转换方式 —— 数组颠倒顺序

    假设我们是小端模式,且远端是采用大端模式的,则从远端接收到的字节数组就需要转换,我们可以直接使用数组的倒转API即可

    1
    2
    3
    4
    5
    6
    int i = 99;
    byte[] bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder(i));
    //假设我们是小端模式,且远端是采用大端模式的,则从远端接收到的字节数组就需要转换
    if (BitConverter.IsLittleEndian)
    Array.Reverse(bytes);
    print(BitConverter.ToInt32(bytes));

    image