精华内容
下载资源
问答
  • 字节顺序
    千次阅读
    2018-08-09 11:26:24

    字节顺序

    2015年06月23日 10:53:45

    阅读数:568

    本篇文章主要介绍字节顺序的相关概念和应用,主要面向整数数据和Unicode字符集的来说明的。 
    1. Endian、位序、存储顺序、阅读顺序 
    1) Endiian 
    “Endian”一词来源于乔纳森•斯威夫特的小说格列佛游记。小说中,小人国为水煮蛋该从大的一端(Big-End)剥开还是小的一端(Little-End)剥开而争论,争论的双方分别被称为Big-endians和Little-endians。 
    Endian翻译为“字节序”,又称端序,尾序。在计算机科学领域中,字节序是指存放多字节数据的字节(byte)的顺序,典型的情况是整数在内存中的存放方式和网络传输的传输顺序。 
    2) 位序(Endianness) 
    位序可以用指位序(bit),位序是对于单字节数据来说的,对于单一的字节(byte),大部分处理器以相同的顺序处理位元(bit),因此单字节的存放方法和传输方式一般相同。大于一个字节的数据在内存中的存放顺序才会讨论字节顺序问题,一个字节的数据当然就无需谈顺序的问题。 
    3) 存储顺序 
    存储顺序是指存储在计算机内存中的地址顺序。例如,一个4字节整形数据0x0A0B0C0D,存储在CPU为intel的x86架构计算机的内存中,高字节数据(0x0A)存储在高地址,低字节数据(0x0D)存储在低地址。 
    4) 阅读顺序 
    阅读顺序就是人们习惯的看文字的顺序。还用“存储顺序”中的4字节整形数据来说明,0x0A0B0C0D为16进制表示形式,阅读顺序为从左到右(高字节到低字节)。在调试程序或编写程序经常看到或用到这样的常量,其实就是一个数据的表示形式,这个形式遵从人们的阅读习惯。 
    2. Little Endian 与 Big Endian 
    继续用上述4字节整形数据0x0A0B0C0D举例。从字面讲,Little Endian就是说小、端,即小数据(此处的小为阅读顺序中的低字节数据:0x0D)存储在前端(内存中的低地址),剩下3个字节的数据依次按照阅读顺序高字节的数据存储在内存中的高地址,如图1。Big Endian就是说大、端,即大数据(此处的大为阅读顺序中的高字节数据:0x0A)存储在前端(内存中的低地址),剩下3个字节的数据依次按照阅读顺序低字节的数据存储在内存中的高地址,如图2。 
    地址:高 -> 低, 0A 0B 0C 0D 
    图1 little endian 
    地址:高 -> 低, 0D 0C 0B 0A 
    图2 big endian 
    3. 字节顺序在Unicode编码存储中的应用 
    Unicode编码在不同的CPU架构的计算机中,存储采用Little Endian或Big Endian。例如:“汉”字的Unicode编码是0x6C49。如果在intel的x86架构的计算机中,将49存储在低地址,是little endian;在老式AMD系列的CPU(很老很老的那种,因为最新的AMD系列已经是x86架构了)架构的计算机中,,将6C存储在低地址,是big endian。 
    4. 字节顺序在网络传输中的应用 
    在大部分的开发中我们不需要考虑字节顺序的问题。唯有在跨平台以及网络程序应用中字节顺序才是一个应该被考虑的问题。 
    1) 网络应用的字节顺序 
    网络字节顺序是TCP/IP规定好的一种数据表示格式,它与具体的CPU类型、操作系统无关,从而可以保证数据在不同主机之间传输时能被正确解释。网络字节顺序采用big endian排序方式。在网络编程时,并不是什么时候都要考虑字节顺序问题。那么什么时候需要考虑呢? 
    实际上如果是应用层的数据,即对TCP/IP来说是透明的数据,不用考虑字节顺序的问题。因为接收端收到的顺序是和发送端一致的。 
    但对于TCP/IP关心的数据(IP地址、端口)来说就不一样了。例如我指定了一个端口号:unsigned short port = 0x0012 (十进制18),把这个端口号传个TCP/IP建立一个socket连接。 
    sockaddr_in addr; 
    addr.sin_family = AF_INET; //使用互联网际协议,即IP协议 
    addr.sin_port = port; 
    addr.sin_addr.S_un.S_addr = htonl(INADDR_ANY); 
    因为网络字节顺序是big endian,即低地址存放的是数值的高位,所以TCP/IP实际上把这个port解释为0x1200(十进制4608)。本来打算是要在端口18建立连接的,但TCP/IP协议栈却在端口4608建立了连接。 
    当一个数据是应用层和TCP/IP都需要关心的时候,需要考虑这个数据的字节顺序(一般而言就是IP地址和端口号)。 
    2) 字节顺序转换函数 
    htons 把 unsigned short 类型从主机序转换到网络序 
    htonl 把 unsigned long 类型从主机序转换到网络序 
    ntohs 把 unsigned short 类型从网络序转换到主机序 
    ntohl 把 unsigned long 类型从网络序转换到主机序 
    这几个函数很好记,比如htons中hton代表host to network, s代表unsigned short 
    char FAR * inet_ntoa( struct in_addr in); 
    将一个IP转换成一个互联网标准点分格式的字符串。 
    in_addr_t inet_addr(const char *cp); 
    将一个点分十进制的IP转换成一个长整数型数(u_long类型)。返回值已是网络字节顺序,可以直接作为internet 地址。

    更多相关内容
  • Delphi 字节顺序交换,通用版,可以对任意长度内存字节顺序进行交换。
  • 如果字符串具有字节顺序标记(BOM),则返回true 安装 npm i has-bom-string --save 用法 有关更多用例,请参见 var hasBomString = require ( 'has-bom-string' ) hasBomString ( '\ufefffoo' ) // => true ...
  • 网络字节顺序和主机字节顺序的转换(htons ntohs htonl ntohl) 注:这是我在网上看到的一篇很好的文章,拿出来分享........ 什么是网络字节顺序和主机字节顺序呢? 在进行网络编程时,需要进行转换以统一“格式” ...

    from  https://blog.csdn.net/baidu_33621692/article/details/53213002

    网络字节顺序和主机字节顺序的转换(htons ntohs htonl ntohl)

    注:这是我在网上看到的一篇很好的文章,拿出来分享........

    什么是网络字节顺序和主机字节顺序呢?

    在进行网络编程时,需要进行转换以统一“格式”
     

    简述:

    网络字节顺序NBO(Network Byte Order):
    按从高到低的顺序存储,在网络上使用统一的网络字节顺序,可以避免兼容性问题。
    

    主机字节顺序(HBO,Host Byte Order):
    不同的机器HBO不相同,与CPU设计有关

    不同的计算机结构有时使用不同的字节顺序存储数据。例如,基于Intel的计算机存储数据
    的顺序与Macintosh(Motorola)计算机就是相反的。Intel字节顺序称为“Little-Endian”(小端),
    反之Macintosh(Motorola),还有网络上采用标准是“Big-Endian”(大端)。

    Big-Endian一个Word中的高位的Byte放在内存中这个Word区域的低地址处。
    Little-Endian一个Word中的低位的Byte放在内存中这个Word区域的低地址处。


     

    详解:

    不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序

    最常见的有两种

    1. Little endian:将低序字节存储在起始地址

    2. Big endian:将高序字节存储在起始地址

    LE little-endian 最符合人的思维的字节序

    地址低位存储值的低位

    地址高位存储值的高位

    怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说

    低位值小,就应该放在内存地址小的地方,也即内存地址低位

    反之,高位值就应该放在内存地址大的地方,也即内存地址高位

     

    BE big-endian

    最直观的字节序

    地址低位存储值的高位

    地址高位存储值的低位

    为什么说直观,不要考虑对应关系

    只需要把内存地址从左到右按照由低到高的顺序写出

    把值按照通常的高位到低位的顺序写出

    两者对照,一个字节一个字节的填充进去

    例子:在内存中双字0x01020304(DWORD)的存储方式

    内存地址

    4000 4001 4002 4003

    LE 04 03 02 01

    BE 01 02 03 04

    例子:如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为

    big-endian little-endian

    0x0000 0x12 0xcd

    0x0001 0x23 0xab

    0x0002 0xab 0x34

    0x0003 0xcd 0x12

    x86系列CPU都是little-endian的字节序.

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

    为了进行转换 bsd socket提供了转换的函数 有下面四个

    htons 把unsigned short类型从主机序转换到网络序

    htonl 把unsigned long类型从主机序转换到网络序

    ntohs 把unsigned short类型从网络序转换到主机序

    ntohl 把unsigned long类型从网络序转换到主机序

    在使用little endian的系统中 这些函数会把字节序进行转换

    在使用big endian类型的系统中 这些函数会定义成空宏

    同样 在网络程序开发时 或是跨平台开发时 也应该注意保证只用一种字节序 不然两方的解释不一样就会产生bug.

    函数例子解析之htonl()

    简述:
        将主机的无符号长整形数转换成网络字节顺序。
        #include
        u_long PASCAL FAR htonl( u_long hostlong);
        hostlong:主机字节顺序表达的32位数。
    注释:
        本函数将一个32位数从主机字节顺序转换成网络字节顺序。
    返回值:
        htonl()返回一个网络字节顺序的值。

    inet_ntoa()

    简述:
    将网络地址转换成“.”点隔的字符串格式。
       #include
       char FAR* PASCAL FAR inet_ntoa( struct in_addr in);
       in:一个表示Internet主机地址的结构。
    注释:
       本函数将一个用in参数所表示的Internet地址结构转换成以“.” 间隔的诸如a.b.c.d”的字符串形式。请注意inet_ntoa()返回的字符串存放在WINDOWS套接口实现所分配的内存中。应用程序不应假设该 内存是如何分配的。在同一个线程的下一个WINDOWS套接口调用前,数据将保证是有效。
    返回值:
    若无错误发生,inet_ntoa()返回一个字符指针。否则的话,返回NVLL。其中的数据应在下一个WINDOWS套接口调用前复制出来。
     
    网 络中传输的数据有的和本地字节存储顺序一致,而有的则截然不同,为了数据的一致性,就要把本地的数据转换成网络上使用的格式,然后发送出去,接收的时候也 是一样的,经过转换然后才去使用这些数据,基本的库函数中提供了这样的可以进行字节转换的函数,如和htons( ) htonl( ) ntohs( ) ntohl( ),这里n表示network,h表示host,htons( ) htonl( )用于本地字节向网络字节转换的场合,s表示short,即对2字节操作,l表示long即对4字节操作。同样ntohs( )ntohl( )用于网络字节向本地格式转换的场合。
    附注:

    一、字节序定义

    字节序,顾名思义字节的顺序,再多说两句就是大于一个字节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了)。

    其实大部分人在实际的开发中都很少会直接和字节序打交道。唯有在跨平台以及网络程序中字节序才是一个应该被考虑的问题。

    在所有的介绍字节序的文章中都会提到字节序分为两类:Big-Endian和Little-Endian。引用标准的Big-Endian和Little-Endian的定义如下:
    a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
    b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
    c) 网络字节序:4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。这种传输次序称作大端字节序。由于 TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。比如,以太网头部中2字节的“以太网帧类型”,表示后面数据的类型。对于ARP请求或应答的以太网帧类型来说,在网络传输时,发送的顺序是0x08,0x06。在内存中的映象如下图所示:
    栈底 (高地址)
    ---------------
    0x06 -- 低位 
    0x08 -- 高位
    ---------------
    栈顶 (低地址)
    该字段的值为0x0806。按照大端方式存放在内存中。

    二、高/低地址与高低字节

    首先我们要知道我们C程序映像中内存的空间布局情况:在《C专家编程》中或者《Unix环境高级编程》中有关于内存空间布局情况的说明,大致如下图:
    ----------------------- 最高内存地址 0xffffffff
     | 栈底
     .
     .              栈
     .
      栈顶
    -----------------------
     |
     |
    /|/

    NULL (空洞)

    /|/
     |
     |
    -----------------------
                    堆
    -----------------------
    未初始化的数据
    ----------------(统称数据段)
    初始化的数据
    -----------------------
    正文段(代码段)
    ----------------------- 最低内存地址 0x00000000

    以上图为例如果我们在栈上分配一个unsigned char buf[4],那么这个数组变量在栈上是如何布局的呢[注1]?看下图:
    栈底 (高地址)
    ----------
    buf[3]
    buf[2]
    buf[1]
    buf[0]
    ----------
    栈顶 (低地址)

    现在我们弄清了高低地址,接着来弄清高/低字节,如果我们有一个32位无符号整型0x12345678(呵呵,恰好是把上面的那4个字节buf看成一个整型),那么高位是什么,低位又是什么呢?其实很简单。在十进制中我们都说靠左边的是高位,靠右边的是低位,在其他进制也是如此。就拿 0x12345678来说,从高位到低位的字节依次是0x12、0x34、0x56和0x78。

    高低地址和高低字节都弄清了。我们再来回顾一下Big-Endian和Little-Endian的定义,并用图示说明两种字节序:
    以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value:
    Big-Endian: 低地址存放高位,如下图:
    栈底 (高地址)
    ---------------
    buf[3] (0x78) -- 低位
    buf[2] (0x56)
    buf[1] (0x34)
    buf[0] (0x12) -- 高位
    ---------------
    栈顶 (低地址)

    Little-Endian: 低地址存放低位,如下图:
    栈底 (高地址)
    ---------------
    buf[3] (0x12) -- 高位
    buf[2] (0x34)
    buf[1] (0x56)
    buf[0] (0x78) -- 低位
    ---------------
    栈顶 (低地址)

    在现有的平台上Intel的X86采用的是Little-Endian,而像Sun的SPARC采用的就是Big-Endian。

    三、例子

    嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。

    例如,16bit宽的数0x1234在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

    内存地址  存放内容
     0x4001    0x12
     0x4000    0x34

    而在Big-endian模式CPU内存中的存放方式则为:

    内存地址  存放内容
     0x4001    0x34
     0x4000    0x12
     
    32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:

    内存地址  存放内容
     0x4003     0x12
     0x4002     0x34
     0x4001     0x56
     0x4000     0x78
     
    而在Big-endian模式CPU内存中的存放方式则为:

    内存地址  存放内容
     0x4003     0x78
     0x4002     0x56
     0x4001     0x34
     0x4000     0x12

    四。

    不同的CPU上运行不同的操作系统,字节序也是不同的,参见下表。
    处理器    操作系统    字节排序
    Alpha    全部     Little endian
    HP-PA     NT     Little endian
    HP-PA     UNIX     Big endian
    Intelx86    全部     Little endian <-----x86系统是小端字节序系统
    Motorola680x()    全部     Big endian
    MIPS     NT     Little endian
    MIPS     UNIX     Big endian
    PowerPC     NT     Little endian
    PowerPC    非NT     Big endian   <-----PPC系统是大端字节序系统
    RS/6000     UNIX     Big endian
    SPARC     UNIX     Big endian
    IXP1200 ARM核心    全部     Little endian

     五、代码实例说明

    我们先看下面的代码,看完啥都明白了。
      
      这是运行在HP-UNIX 9000/800下完整的C语言代码,即为 Big-Endian 方式。
      
      #include
      void main()
      {
      
      int i=0x41424344;
      
      printf("int  Address:%x Value:%x/n",&i,i);
      printf("-------------------------------/n");
      
      char* pAddress=(char*)&i;
      int j;
      
      for(j=0;j<=3;j++)
      {
       printf("char Address:%x Value:%c/n",pAddress,*pAddress);
       pAddress++;
      }
      
      }
      
      编译输出(cc -g ...):
      
      int  Address:7f7f08f0 Value:41424344
      -------------------------------
      char Address:7f7f08f0 Value:A
      char Address:7f7f08f1 Value:B
      char Address:7f7f08f2 Value:C
      char Address:7f7f08f3 Value:D

        我们回到Windows XP下,看看这段代码的输出。Little-Endian 模式。
        
      #include
      void main()
      {
       int i=0x41424344;
       printf("int  Address:%x Value:%x/n",&i,i);
       printf("-------------------------------/n");
       char* pAddress=(char*)&i;
       int j;
       for(j=0;j<=3;j++)
       {
        printf("char Address:%x Value:%c/n",pAddress,*pAddress);
        pAddress++;
       }
      }

      编译输出(VC 6.0):
      
      int  Address:12ff7c Value:41424344
      -------------------------------
      char Address:12ff7c Value:D
      char Address:12ff7d Value:C
      char Address:12ff7e Value:B
      char Address:12ff7f Value:A
       
      

        看完上面代码,应该就很清楚了,什么字节顺序?真是简单的要死!int i=0x41424344;
        采用16进制,我们知道A的ACSII码是65,16进制就是41,可以理解,本例是想通过输出
        A,B,C,D来验证字节顺序。我再对内存数据进行列表,相信会更有深层次的理解。
       

      Big-Endian的内存放置顺序如下:
      
      地址:0x7f7f08f0  0x7f7f08f1  0x7f7f08f2  0x7f7f08f3
      
            0x41        0x42        0x43        0x44
          
      Little-Endian的内存放置顺序如下:

      地址:0x0012ff7c  0x0012ff7d  0x0012ff7e  0x0012ff7f
      
            0x44        0x43        0x42        0x41

     六、用函数判断系统是Big Endian还是Little Endian

    bool IsBig_Endian()
    //如果字节序为big-endian,返回true;
    //反之为   little-endian,返回false
    {
        unsigned short test = 0x1122;
        if(*( (unsigned char*) &test ) == 0x11)
           return TRUE;
    else
        return FALSE;

    }//IsBig_Endian()

     

    七、最后的说明

      主机字节顺序(Host)
      Little-Endian [ Intel、VAX和Unisys处理器 等]
      网络字节顺序(Network)
      Big-Endian [ IBM 370、Motorola和大多数RISC设计 ---- IBM 大型机和大多数Unix平台 ]

      字节转换多半应用在网络编程,或者代码移植的情况下。
      Unix环境下的一些相关函数:(必须包含头文件 #include )
      
      htons()--"Host to Network Short"
      htonl()--"Host to Network Long"
      ntohs()--"Network to Host Short"
      ntohl()--"Network to Host Long"
      
      Windows .Net 一些相关函数:
      
      HostToNetworkOrder
      NetworkToHostOrder

    补充:ACE用ACE_InputCDR和ACE_OutputCDRCDR处理字节序和字节对齐问题。

    展开全文
  • 网络字节顺序与主机字节顺序转换

    千次阅读 2021-02-23 10:02:03
    网络字节顺序与主机字节顺序转换 1.为什么要进行转换? 在进行网络编程时,由于网络字节的顺序和主机的字节顺序可能存在不同,需要进行转换以统一“格式” 2.什么是网络字节顺序和主机字节顺序呢? 2.1网络字节顺序...

    网络字节顺序与主机字节顺序转换

    1.为什么要进行转换?

    在进行网络编程时,由于网络字节的顺序和主机的字节顺序可能存在不同,需要进行转换以统一“格式”

    2.什么是网络字节顺序和主机字节顺序呢?

    2.1网络字节顺序NBO(Network Byte Order):

    网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式,统一格式可以避免兼容性问题。

    2.2主机字节顺序(HBO,Host Byte Order):

    不同的机器的主机字节顺序(HBO)不一定相同,与CPU设计有关。
    详解
    不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序
    最常见的有两种
    1. Little-endian:将低序字节存储在起始地址
    2. Big-endian :将高序字节存储在起始地址
    LE(little-endian)将地址低位存储值的低位,将地址高位存储值的高位,是最符合人的思维的字节序。因为从人的思维来说低位值小,就应该放在内存地址小的地方,也即内存地址低位;反之,高位值大就应该放在内存地址大的地方,也即内存地址高位。
    BE(big-endian)的地址低位存储的是值的高位,地址高位存储的是值的低位,它是最直观的字节序。说直观是它不需要考虑对应关系,只需要把内存地址从左到右按照由低到高的顺序写出;把值按照通常的高位到低位的顺序写出,两者对照,一个字节一个字节的填充进去。例如:
    在内存中双字0x01020304的存储方式
    内存地址
    4000 4001 4002 4003
    LE 04 03 02 01
    BE 01 02 03 04
    如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为
    内存地址BE LE
    0x0000 0x12 0xcd
    0x0001 0x23 0xab
    0x0002 0xab 0x34
    0x0003 0xcd 0x12
    通常来说,x86 cpu是Little-Endian。而一般ARM CPU也是Little-Endian。
    但是当前常见的开发板使用的处理器比如S3C2410A、S3C2440等都是大小端支持的,可以通过软件选择。

    3.如何进行网络字节顺序和主机字节顺序的转换?

    网络中传输的数据有的和本地字节存储顺序一致,而有的则截然不同,为了数据的一致性,就要把本地的数据转换成网络上使用的格式,然后发送出去,接收的时候也是一样的,经过转换然后才去使用这些数据,基本的库函数中提供了这样的可以进行字节转换的函数,如和htons( ) htonl( ) ntohs( ) ntohl( ),这里n表示network,h表示host,htons( ) htonl( )用于本地字节向网络字节转换的场合,s表示short,即对2字节操作,l表示long即对4字节操作。同样ntohs( )ntohl( )用于网络字节向本地格式转换的场合
    1、htons 把unsigned short类型从主机序转换到网络序
    2、htonl 把unsigned long类型从主机序转换到网络序
    3、ntohs 把unsigned short类型从网络序转换到主机序
    4、ntohl 把unsigned long类型从网络序转换到主机序
    在使用little endian的系统中 这些函数会把字节序进行转换,在使用big endian类型的系统中这些函数会定义成空宏。

    其他类型字节序转换
    long long 类型的网络字节顺序转换
    可以通过位移的方式实现

    4.拓展部分(详细介绍)

    4.1字节序:

    字节序就是字节的顺序,就是大于一个字节类型的数据在内存中的存放顺序(一个字节的数据当然就无需谈顺序的问题了)。
    其实大部分人在实际的开发中都很少会直接和字节序打交道。唯有在跨平台以及网络程序中字节序才是一个应该被考虑的问题。
    字节序分为两类:Big-Endian和Little-Endian。引用标准的Big-Endian和Little-Endian的定义如下:
    a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
    b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
    c) 网络字节序:4个字节的32 bit值以下面的次序传输:首先是0~7bit,其次8~15bit,然后16~23bit,最后是24~31bit。这种传输次序称作大端字节序。由于 TCP/IP首部中所有的二进制整数在网络中传输时都要求以这种次序,因此它又称作网络字节序。比如,以太网头部中2字节的“以太网帧类型”,表示后面数据的类型。对于ARP请求或应答的以太网帧类型来说,在网络传输时,发送的顺序是0x08,0x06。在内存中的映象如下所示:

    栈底 (高地址)

    0x06 – 低位
    0x08 – 高位

    栈顶 (低地址)
    该字段的值为0x0806。按照大端方式存放在内存中。

    4.2高/低地址与高/低字节

    首先我们要知道我们C程序映像中内存的空间布局情况:在《C专家编程》中或者《Unix环境高级编程》中有关于内存空间布局情况的说明,大致如下图:

    ----------------------- 最高内存地址 0xffffffff

    | 栈底

    .
    . 栈

    .
    栈顶


    |
    |
    /|/
    NULL (空洞)
    /|/
    |


    未初始化的数据

    ----------------(统称数据段)

    初始化的数据


    正文段(代码段)
    ----------------------- 最低内存地址 0x00000000

    以上图为例如果我们在栈上分配一个unsigned char buf[4],那么这个数组变量在栈上是如何布局的呢?看下图:

    栈底 (高地址)


    buf[3]
    buf[2]
    buf[1]
    buf[0]


    栈顶 (低地址)

    现在我们弄清了高低地址,接着来弄清高/低字节,如果我们有一个32位无符号整型0x12345678(恰好是把上面的那4个字节buf看成一个整型),那么高位是什么,低位又是什么呢?其实很简单。在十进制中我们都说靠左边的是高位,靠右边的是低位,在其他进制也是如此。就拿 0x12345678来说,从高位到低位的字节依次是0x12、0x34、0x56和0x78。
    高低地址和高低字节都弄清了。我们再来回顾一下Big-Endian和Little-Endian的定义,并用图示说明两种字节序:以unsigned int value = 0x12345678为例,分别看看在两种字节序下其存储情况,我们可以用unsigned char buf[4]来表示value:
    Big-Endian: 低地址存放高位,如下图:

    栈底 (高地址)


    buf[3] (0x78) – 低位
    buf[2] (0x56)
    buf[1] (0x34)
    buf[0] (0x12) – 高位


    栈顶 (低地址)

    Little-Endian: 低地址存放低位,如下图:

    栈底 (高地址)


    buf[3] (0x12) – 高位
    buf[2] (0x34)
    buf[1] (0x56)
    buf[0] (0x78) – 低位


    栈顶 (低地址)

    4.3例子

    嵌入式系统开发者应该对Little-endian和Big-endian模式非常了解。采用Little-endian模式的CPU对操作数的存放方式是从低字节到高字节,而Big-endian模式对操作数的存放方式是从高字节到低字节。
    例如,16bit宽的数0x1234在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
    内存地址 存放内容
    0x4001 0x12
    0x4000 0x34

    而在Big-endian模式CPU内存中的存放方式则为:
    内存地址 存放内容
    0x4001 0x34
    0x4000 0x12
    32bit宽的数0x12345678在Little-endian模式CPU内存中的存放方式(假设从地址0x4000开始存放)为:
    内存地址 存放内容
    0x4003 0x12
    0x4002 0x34
    0x4001 0x56
    0x4000 0x78
    而在Big-endian模式CPU内存中的存放方式则为:
    内存地址 存放内容
    0x4003 0x78
    0x4002 0x56
    0x4001 0x34
    0x4000 0x12

    4.4不同的CPU上运行不同的操作系统,字节序也是不同的

    部分情况参见下表。
    处理器 操作系统 字节排序
    Alpha 全部 Little endian
    HP-PA NT Little endian
    HP-PA UNIX Big endian
    Intelx86 全部 Little endian <-----x86系统是小端字节序系统
    Motorola680x() 全部 Big endian
    MIPS NT Little endian
    MIPS UNIX Big endian
    PowerPC NT Little endian
    PowerPC 非NT Big endian <-----PPC系统是大端字节序系统
    RS/6000 UNIX Big endian
    SPARC UNIX Big endian
    IXP1200 ARM核心 全部 Little endian

    展开全文
  • 里面也添加了对字节顺序的处理逻辑。如果是通信的数据包可以启动字节顺序转换。 需要注意的一点是我对输入的每一个元素只拷贝元素内容,没有存储其类型,所以在字节流输出的时候没有办法做类型校验,这样对于结构经

    介于要设计游戏服务器和客户端逻辑,现在需要普及一下我在代码中用到的一些数据结构,这一篇讲一下字节流--- ByteStream。

    字节流就是自己实现的一个数据存储区,可对基础数据和其他特定数据进行序列化和反序列化。用与玩家数据或者其他服务器数据的落地保存。也可用于客户端和服务器,、服务器和服务器之间的通信。里面也添加了对字节顺序的处理逻辑。如果是通信的数据包可以启动字节顺序转换。

    需要注意的一点是我对输入的每一个元素只拷贝元素内容,没有存储其类型,所以在字节流输出的时候没有办法做类型校验,这样对于结构经常变化的数据集合不是很友好,所以如果在通信层面用的话,对于不是经常变化结构的数据集合使用这种方法好于protobuf, 对于经常变化的数据集合可以使用protobuf, 但是我的服务器实现里面是模块化封装,底层的数据结构不会发生改变,所有会用ByteStream, 而 protobuf 会给上层业务层面使用。

    贴一下代码:

    1:字节顺序代码:

    // 字节顺序转换
    
    #ifndef EndianConverter_h__
    #define EndianConverter_h__
    
    #include <vector>
    
    namespace SCore
    {
    
    	// 未知
    #define ENDAIN_UNKNOW 0
    
    // 大端 (网络字节顺序)
    #define ENDIAN_BIG 1	
    
    // 小端 (本地字节顺序)
    #define ENDIAN_LITTLE 2
    
    
    // 每一个包含头文件的cpp 都会生成 endian_struct ,目前只有一个文件包含
    static union endian_struct
    {
    	char buf[sizeof(int)];
    	int i;
    } g_endian_t = { {(char)ENDIAN_LITTLE , (char)3, (char)5, (char)ENDIAN_BIG } };
    
    // 本机字节顺序
    #define LOCAL_ENDIAN (g_endian_t.i & 0xff)
    
    // 字节顺序反转
    template<int N> void EndianReverse(char* value)
    {
    	if (value == (char*)0 || N <= 1)
    		return;
    
    	for (int i = 0; i < N / 2; ++i)
    	{
    		std::swap(value[i], value[N - i - 1]);
    	}
    }
    
    // 字节顺序转换
    template<typename T> void EndianConverter(T* value)
    {
    	EndianReverse<sizeof(T)>((char*)value);
    }
    
    // 网络字节顺序转换成本机字节顺序
    template<typename T> void NetEndianToLocal(T* value)
    {
    	if (ENDIAN_BIG == LOCAL_ENDIAN)
    		return;
    	EndianReverse<sizeof(T)>((char*)value);
    }
    
    // 本机字节顺序转换成网络字节顺序
    template<typename T> void LocalEndianToNet(T* value)
    {
    	if (ENDIAN_BIG == LOCAL_ENDIAN)
    		return;
    	EndianReverse<sizeof(T)>((char*)value);
    }
    
    // 顺序转换
    template <int N>
    void ENDIAN_SWAP(char* ptr)
    {
    	for (int i = 0; i < N / 2; ++i)
    	{
    	    std::swap(ptr[i], ptr[N - i - 1]);
    	}
    }
    }

    2:字节流代码:

    
    // 字节流
    #ifndef ByteStream_h__
    #define ByteStream_h__
    
    #include <vector>
    #include <assert.h>
    #include <string>
    #ifndef _WIN32
    #include <string.h>
    #endif
    
    #include "Net/EndianConverter.h"
    
    namespace SCore
    {
    
    #define INIT_BYTE_SIZE 128
    	class ByteStream
    	{
    		typedef std::vector<char> ByteData;
    		typedef char* ByteStreamChar;
    		typedef wchar_t* ByteStreamWChar;
    
    	public:
    		ByteStream(size_t initSize = INIT_BYTE_SIZE, bool bEndian = false)
    		{
    			m_data.reserve(initSize);
    			m_nMaxSize = initSize;
    			m_nUseSize = 0;
    			m_nReadOffset = 0;
    			m_bEndian = bEndian;
    			*this << m_bEndian;
    		}
    
    		ByteStream(const ByteStream& cpy) :
    			m_nMaxSize(cpy.m_nMaxSize),
    			m_nReadOffset(cpy.m_nReadOffset),
    			m_nUseSize(cpy.m_nUseSize)
    		{
    			m_data.reserve(m_nMaxSize);
    			memcpy(m_data.data(), cpy.m_data.data(), m_nUseSize);
    		}
    
    		ByteStream(const char* cs, size_t len)
    		{
    			m_nMaxSize = 0;
    			m_nUseSize = 0;
    			m_nReadOffset = 0;
    			LoadData(cs, len);
    		}
    
    
    		// 获取流数据
    		const char* GetData()
    		{
    			return m_data.data();
    		}
    
    		// 获取流数据大小
    		size_t GetDataLen()
    		{
    			return m_nUseSize;
    		}
    
    		// 加载数据
    		void LoadData(const char* ptr, size_t len)
    		{
    			m_nUseSize = 0;
    			Append(ptr, len);
    			ResetReadOffset();
    		}
    
    		// 重置读偏移
    		void ResetReadOffset()
    		{
    			m_nReadOffset = 0;
    			*this >> m_bEndian;
    		}
    
    		// 清理
    		void Clear()
    		{
    			m_nUseSize = 0;
    		}
    
    	public:
    		// 输入
    		template<typename T>
    		ByteStream& operator<<(const T& t)
    		{
    			if (m_bEndian)
    			{
    				T tmp = t;
    				LocalEndianToNet<T>(&tmp);
    				Append((const char*)&tmp, sizeof(t));
    				return *this;
    			}
    
    			Append((const char*)&t, sizeof(t));
    			return *this;
    		}
    
            // 特例重载
    		ByteStream& operator<<(const char* t)
    		{
    			*this << std::string(t);
    			return *this;
    		}
    
    		ByteStream& operator<<(const wchar_t* t)
    		{
    			*this << std::wstring(t);
    			return *this;
    		}
    
            // 特例化模板
    		template<>
    		ByteStream& operator<<(const ByteStreamChar& t)
    		{
    			*this << std::string(t);
    			return *this;
    		}
    
    		template<>
    		ByteStream& operator<<(const ByteStreamWChar& t)
    		{
    			*this << std::wstring(t);
    			return *this;
    		}
    
    		template<>
    		ByteStream& operator<<(const std::string& t)
    		{
    			Append(t.c_str(), t.length() + 1);
    			return *this;
    		}
    
    		template<>
    		ByteStream& operator<<(const std::wstring& t)
    		{
    			Append((const char*)t.c_str(), (t.length() + 1) * sizeof(wchar_t));
    			return *this;
    		}
    
    		// 输出
    		template<typename T>
    		ByteStream& operator>>(T& t)
    		{
    			if (m_nUseSize - m_nReadOffset < sizeof(t))
    			{
    				assert(0 && "read byte stream faild...");// ERROR
    				return *this;
    			}
    
    			memcpy(&t, m_data.data() + m_nReadOffset, sizeof(t));
    			m_nReadOffset += sizeof(t);
    
    			if (m_bEndian)
    			{
    				NetEndianToLocal<T>(&t);
    			}
    
    			return *this;
    		}
    
            // 特例化模板
    		template<>
    		ByteStream& operator>>(std::string& t)
    		{
    			size_t len = strlen(m_data.data() + m_nReadOffset);
    			if (len >= m_nUseSize - m_nReadOffset)
    			{
    				assert(0 && "read byte stream faild...");// ERROR
    				return *this;
    			}
    
    			t = (m_data.data() + m_nReadOffset);
    			m_nReadOffset += (len + 1);
    			return *this;
    		}
    
    		template<>
    		ByteStream& operator>>(std::wstring& t)
    		{
    			size_t len = (strlen(m_data.data() + m_nReadOffset) + 1) * sizeof(wchar_t);
    			if (len >= m_nUseSize - m_nReadOffset)
    			{
    				assert(0 && "read byte stream faild...");// ERROR
    				return *this;
    			}
    
    			t = (wchar_t*)(m_data.data() + m_nReadOffset);
    			m_nReadOffset += len;
    			return *this;
    		}
    
    	private:
    		// 追加写
    		void Append(const char* ptr, size_t len)
    		{
    			assert(ptr && len > 0);
    			if (!ptr || len == 0)
    			{
    				return;
    			}
    
    			if (m_nMaxSize - m_nUseSize < len)
    			{
    				// 扩充
    				size_t newSize = m_nMaxSize + (m_nMaxSize >> 1);
    				if (newSize < len + m_nUseSize)
    				{
    					newSize = len + m_nUseSize;
    				}
    
    				m_data.reserve(newSize);
    				m_nMaxSize = newSize;
    			}
    
    			memcpy(m_data.data() + m_nUseSize, ptr, len);
    			m_nUseSize += len;
    
    		}
    
    	private:
    
    		ByteData m_data;
    		size_t m_nMaxSize;
    		size_t m_nUseSize;
    		size_t m_nReadOffset;	// 读取偏移	
    		bool m_bEndian;
    	};
    }
    
    #endif // ByteStream_h__
    

    // 简单使用示例:

    // 通信包使用ByteStream:
    
    void SendMsg()
    {
        // 定义零时变量,初始化buffer大小128, 开启字节顺序转换
        ByteStream bs(128, true);
    
        // 要发送的数据
        time_t now = g_ServerCore.GetNow();
        int id = 1;
        std::string name("xxxxxxx");
        long long data = 2;
      
    
        // 输入数据
    	bs << now;
        bs << id << name << data;
    
        // 发包
        // void send(int msg_type, const char* msg, size_t msg_len);
        #define msg_type_test 2    // 定义消息类型,这里都是演示
    	pSession->Send(msg_type_test , bs.GetData(), bs.GetDataLen());
    }
    
    // 收包:
    void OnRecvMsg(MsgPacket* msg)
    {
    	assert(msg);
    
        // msg->msg : 上面发送的bs.GetData()
        // msg->len  : 上面发送的bs.GetDataLen()
        ByteStream bs(msg->msg, msg->len);
    
        // 数据输出
    	time_t now;
    	int id = 1;
        std::string name;
        long long data;
    	bs >> now >> id >> name >> data;
        // TODO
    }
    
    // 用于数据存储是一样的使用方式,只不过不需要字节顺序转换
    ByteStream bs;    // 这样定义就可以,

    展开全文
  • 网络字节顺序和主机字节顺序的转换 通过这篇文章你会知道什么就小尾方式和大尾 方式 网络字节顺序其实就是大尾方式,intel系统一般是小尾 方式
  • 组态王中Modbus字节顺序的调整

    千次阅读 2021-08-25 13:21:28
    组态王通过modbus协议读取设备的浮点数和Long型的整数,有时候会出现数值很大,或者为0的情况,这时候就需要做字节顺序转换。本文重点介绍三种调整Modbus字节顺序的方法。 1.建Swap寄存器 利用给寄存器swapF0等特殊...
  • 本文章是关于字节顺序的。
  • Modbus通讯协议中的四种字节顺序

    千次阅读 2021-01-04 01:15:05
    使用IEEE 754规范,如显示不正常可进行 字节顺序 交换位置即可。如下: Float Big-endian 字节顺序:A B C D Float Little-endian 字节顺序:D C B A Float Big-endian byte swap字节顺序:B A D C Float Little-endian...
  • 字节顺序标记(ByteOrderMark)BOM

    千次阅读 2018-12-20 16:52:56
    之前我们整理了大端和小端 和 字符编码 ,知道对于多字节的数据会存在不同机器之间的存储问题。对于整形我们知道可以通过网络字节序进行传输,但是对于不同编码的字符串我们该怎么办呢? 其实字符串就是一连串的内存...
  • 字节顺序标记(BOM)详解

    千次阅读 2019-05-25 23:36:35
    BOM:即“Byte Order Mark”的缩写,翻译出来就是字节顺序标记(BOM)的意思 二、 在UCS 编码中有一个叫做"ZERO WIDTH NO-BREAK SPACE"的字符,它的编码是FEFF。而FFFE在UCS中是不存在的字符,所以不应该出现在...
  • ntohs、ntohl、htons和htonl大小端CPU大小端之分常见字节序ntohs、ntohl、htons和htonl含义Linux系统下定义Windows系统下ntohs()htons()htonl()和ntohl()IP地址格式转换函数inet_aton、inet_addr、inet_ntoa函数(已...
  • Java非字节类型的基本类型,除了布尔型都是由组合在一起的几个字节组成的。这些数据类 型及其大小总结在下表 中: 数据类型 大小(以字节表示) Byte 1 Char 2 Short 2 Int 4 Long 8 Float 4 ...
  • kettle同步数据表时报错:无效的 “UTF8” 编码字节顺序: 0x00 问题描述 通过navicat查询该条数据显示为乱码,翻阅百度PostgreSQL不支持存储NULL(\ 0×00)的文本字段中的字符 通过Python psycopg2查询得到字段原始...
  • 字节顺序定义 字节顺序是指占内存多于一个字节类型的数据在内存中的存放顺序,通常有小端、大端两种字节顺序。小端字节序指低字节数据存放在内存低地址处,高字节数据存放在内存高地址处;大端字节序是高字节数据...
  • 今天使用F12调试的时候提示HTML1114: (UNICODE 字节顺序标记)的代码页 utf-8 覆盖(META 标记)的冲突的代码页 utf-8,需要的朋友可以参考下
  • 大端存储顺序:数据的高字节存储在内存的低地址中,数据的低字节存储在内存的高地址中。 小端存储顺序:数据的高字节 存储在内存的高地址中,数据的低字节存储在内存的低地址中。 存储示意图 例如变量x的类型为...
  • 如何判断机器的字节顺序是高字节在前还是低字节在前 机器的字节顺序是高字节在前还是低字节在前,又称为大端小端。什么是大/小端呢?所谓大端就是指高位值在内存中放低位地址,所谓小端是指低位值在内存中放低位地址...
  • 无效的 "UTF8" 编码字节顺序: 0x00 java.lang.IllegalArgumentException: Null input buffer 此时想到对这段文本进行转码存储。实施起来,转码过程没有问题,也能够存到数据库里了。但是从数据库中读取后,解码时...
  • 数据入库时报错了,一开始还觉得很奇怪呢,难道是我的表字段问题??之前入库都好好的呀。 后来百度一下,才发现是因为入库的数据包含了空格。。。(因为这份数据是测试自己造的) 所以在set时,String.trim() ...
  • 大端:在低地址存放高字节,在高地址存放低字节 小端:在低地址存放低字节,在高地址存放高字节   注意:地址是左边是低地址,右边是高地址,字节是左边是高字节,右边是低字节。采用大端法存储变量和采用小端法...
  • strip-bom, 从字符串中带 utf 8 字节顺序标记( 物料清单) 带 bom 带 UTF-8 字节顺序标记从字符串中删除从维基百科:Unicode标准允许 UTF-8 中的BOM,但不要求它,也不推荐它的使用。 字节顺序在 UTF-8 中没有意义。...
  • 最近搞CLICK编程,使用wireshark抓包,发现字节顺序很诡异,所以研究了一下 网络字节顺序与本地字节顺序之间的转换函数: htonl()--"Host to Network Long" ntohl()--"Network to Host Long" htons()--"Host to ...
  • 字节顺序(大端与小端)概念

    千次阅读 2018-10-02 17:37:58
    字节顺序:高位优先(big-endian)和低位优先(little-endian)  网络字节序: MSB 高字节前存法 Most Significant Bit (Big Edian)   主机字节序: LSB 低字节前存法 Lest Significant Bit (Little Edian)  ...
  • @[TOC](字节顺序:高位优先(big-endian)和低位优先(little-endian)) 网络字节序: MSB 高字节前存法 Most Significant Bit (Big Edian) 主机字节序: LSB 低字节前存法 Lest Significant Bit (Little Edian) 字节...
  • 网络字节顺序

    千次阅读 2014-06-18 09:35:57
    java中对int转byte, 永远是去低8位的字节, 不用去管系统内部字节顺序. 当你传向网络时, 你就要自己进行网络字节顺序发, 先发低位字节,再发高位字节. LE little-endian 最符合人的思维的字节序 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 611,879
精华内容 244,751
关键字:

字节顺序

友情链接: code.rar