精华内容
下载资源
问答
  • IP、主机字节序、网络字节序、互转 ------------------------------------------------------- using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Net; using ...
  • 网络字节序、大小端模式学习记录: 用共用体的方式来测试: 可以通过下面的小程序测试自己的机器是大端字节序还是小端字节序 #include <stdio.h> union { char ch; int i; }un; int main(void) { un.i = 0x...

    多字节数据的存储顺序称为字节序。分为大、小端模式:
    大端:数据高位对应低地址
    小端:数据高位对应高地址
    在这里插入图片描述
    可以通过下面的程序 测试自己的机器是大端字节序还是小端字节序

    用共用体的方式来测试:

    //test_un.c
    #include <stdio.h>
    union{
        char ch;
        int i;
    }un;
    int main(void)
    {
        un.i = 0x12345678;
        if(un.ch == 0x78){
            printf("Little endian\n");
        }
        else if(un.ch == 0x12){
            printf("Big endain\n");
        }else{
        	 printf("error\n");
    	}
        return 0;
    }
    
    代码解析:数据的高低位是对于要存储的数据而言的,高低地址是针对内存来说的(内存以字节为单位)。
    比如:0X12345678 数据的高位是87,低位是12
    大端模式在内存中存放数据:
    低地址-----> 高地址
    0X12 ,0X34 ,0X56 ,0X78
    
    小端模式在内存中存放数据:
    低地址----->高地址
    0X78 ,0X56 ,0X34 ,0X12
    
    cxx@ubuntu16:~/git_proj/tcp_socket$ gcc test_un.c
    cxx@ubuntu16:~/git_proj/tcp_socket$ ./a.out 
    Little endian
    cxx@ubuntu16:~/git_proj/tcp_socket$ 
    

    用指针的方式测试:

    //test_ptr.c
    #include <stdio.h>
    int main(void)
    {
        int a = 0x12345678;
        char *p = NULL;
        p = (char *)&a;
        if(*p == 0x78){
            printf("Little  endain \n");
        }
        else if(*p == 0x12){
            printf("Big endain\n");
        }else{
            printf("error\n");
        }
        return 0;
    }
    
    原理:用指针的方式来测试大小端,本质上和用共用体来测试是一样的。
    假设int型变量的4个字节,在大小端下,各自被赋值为后,4个字节里保存的数据:
    比如:0X12345678 数据的高位是78,低位是12
    大端模式在内存中存放数据:
    低地址-----> 高地址
    0X12 ,0X34 ,0X56 ,0X78
    
    小端模式在内存中存放数据:
    低地址----->高地址
    0X78 ,0X56 ,0X34 ,0X12
    
    cxx@ubuntu16:~/git_proj/tcp_socket$ gcc test_ptr.c 
    cxx@ubuntu16:~/git_proj/tcp_socket$ ./a.out 
    Little  endain 
    cxx@ubuntu16:~/git_proj/tcp_socket$ 
    
    展开全文
  • 不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机字节序,存储方式有2种 1)、大端模式 大端模式(Big-Endian)就是高位字节排放在内存的低地址端(即该值的起始地址),低位字节排放...

    1、主机字节序介绍

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

             1)、大端模式

                            大端模式(Big-Endian)就是高位字节排放在内存的低地址端(即该值的起始地址),低位字节排放在内存的高地址端

             2 ) 、小端模式

                             小端存储在起始地址处,即是小端字节序;大端存储在起始地址处,即是大端字节序; 或者说: 1.小端法(Little-Endian)就是低位字节排放在内存的低地址端(即该值的起始地址),高位字节排放在内存的高地址端。

             3)、常见cpu大小端存储方式

                              不同CPU有不同的字节序类型,典型的使用小端存储的CPU有:Intel x86和ARM  典型的使用大端存储CPU有:Power PC、MIPS UNIX和HP-PA UNIX

     

    2、网络字节序介绍

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

             为什么网络字节序是大端模式?

             网络上传输的数据都是字节流,对于一个多字节数值,在进行网络传输的时候,先传递哪个字节?也就是说,当接收端收到第一个字节的时候,它将这个字节作为高位字节还是低位字节处理,是一个比较有意义的问题; UDP/TCP/IP协议规定:把接收到的第一个字节当作高位字节看待,这就要求发送端发送的第一个字节是高位字节;而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节,也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节(即:高位字节存放在低地址处);由此可见,多字节数值在发送之前,在内存中因该是以大端法存放的; 所以说,网络字节序是大端字节序; 比如,我们经过网络发送整型数值0x12345678时,在80X86平台中,它是以小端发存放的,在发送之前需要使用系统提供的字节序转换函数htonl()将其转换成大端法存放的数值;如下图2所示:

     

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


           相同字节序的平台在进行网络通信时可以不进行字节序转换,但是跨平台进行网络数据通信时必须进行字节序转换。      原因如下:网络协议规定接收到得第一个字节是高字节,存放到低地址,所以发送时会首先去低地址取数据的高字节。小端模式的多字节数据在存放时,低地址存放的是低字节,而被发送方网络协议函数发送时会首先去低地址取数据(想要取高字节,真正取得是低字节),接收方网络协议函数接收时会将接收到的第一个字节存放到低地址(想要接收高字节,真正接收的是低字节),所以最后双方都正确的收发了数据。而相同平台进行通信时,如果双方都进行转换最后虽然能够正确收发数据,但是所做的转换是没有意义的,造成资源的浪费。而不同平台进行通信时必须进行转换,不转换会造成错误的收发数据,字节序转换函数会根据当前平台的存储模式做出相应正确的转换,如果当前平台是大端,则直接返回不进行转换,如果当前平台是小端,会将接收到得网络字节序进行转换。

     

    4、转换API

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

    htons 把unsigned short类型从主机序转换到网络序
    htonl 把unsigned long类型从主机序转换到网络序
    ntohs 把unsigned short类型从网络序转换到主机序
    ntohl 把unsigned long类型从网络序转换到主机序

     

    5、总结

    所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。

    Java和一些windows编程语言如c、c++、delphi所写的网络程序进行通讯时,需要进行相应的转换,java字节序转换类如下

     

    字节序转换类:
    
    /**
    * 通信格式转换
    *
    * Java和一些windows编程语言如c、c++、delphi所写的网络程序进行通讯时,需要进行相应的转换
    * 高、低字节之间的转换
    * windows的字节序为低字节开头
    * linux,unix的字节序为高字节开头
    * java则无论平台变化,都是高字节开头
    */
    
    public class FormatTransfer {
    /**
      * 将int转为低字节在前,高字节在后的byte数组
      * @param n int
      * @return byte[]
      */
    public static byte[] toLH(int n) {
    
       byte[] b = new byte[4];
        b[0] = (byte) (n & 0xff);
        b[1] = (byte) (n >> 8 & 0xff);
        b[2] = (byte) (n >> 16 & 0xff);
        b[3] = (byte) (n >> 24 & 0xff);
        return b;
    }
    
    /**
      * 将int转为高字节在前,低字节在后的byte数组
      * @param n int
      * @return byte[]
      */
    public static byte[] toHH(int n) {
    
       byte[] b = new byte[4];
        b[3] = (byte) (n & 0xff);
        b[2] = (byte) (n >> 8 & 0xff);
        b[1] = (byte) (n >> 16 & 0xff);
        b[0] = (byte) (n >> 24 & 0xff);
        return b;
    }
    
    /**
      * 将short转为低字节在前,高字节在后的byte数组
      * @param n short
      * @return byte[]
      */
    public static byte[] toLH(short n) {
        byte[] b = new byte[2];
        b[0] = (byte) (n & 0xff);
        b[1] = (byte) (n >> 8 & 0xff);
        return b;
    }
    
    /**
      * 将short转为高字节在前,低字节在后的byte数组
      * @param n short
      * @return byte[]
      */
    public static byte[] toHH(short n) {
        byte[] b = new byte[2];
        b[1] = (byte) (n & 0xff);
        b[0] = (byte) (n >> 8 & 0xff);
        return b;
    }
    
     
    
    /**
      * 将将int转为高字节在前,低字节在后的byte数组
    
    public static byte[] toHH(int number) {
        int temp = number;
        byte[] b = new byte[4];
        for (int i = b.length - 1; i > -1; i--) {
            b = new Integer(temp & 0xff).byteValue();
            temp = temp >> 8;
         }
         return b;
    }
    
    public static byte[] IntToByteArray(int i) {
        byte[] abyte0 = new byte[4];
        abyte0[3] = (byte) (0xff & i);
        abyte0[2] = (byte) ((0xff00 & i) >> 8);
        abyte0[1] = (byte) ((0xff0000 & i) >> 16);
        abyte0[0] = (byte) ((0xff000000 & i) >> 24);
        return abyte0;
    }
    
    
    */
    
    /**
      * 将float转为低字节在前,高字节在后的byte数组
      */
    public static byte[] toLH(float f) {
         return toLH(Float.floatToRawIntBits(f));
    }
    
    /**
      * 将float转为高字节在前,低字节在后的byte数组
      */
    public static byte[] toHH(float f) {
         return toHH(Float.floatToRawIntBits(f));
    }
    
    /**
      * 将String转为byte数组
      */
    public static byte[] stringToBytes(String s, int length) {
          while (s.getBytes().length < length) {
              s += " ";
          }
          return s.getBytes();
    }
    
    
    /**
      * 将字节数组转换为String
      * @param b byte[]
      * @return String
      */
    public static String bytesToString(byte[] b) {
        StringBuffer result = new StringBuffer("");
        int length = b.length;
        for (int i=0; i<length; i++) {
            result.append((char)(b & 0xff));
        }
        return result.toString();
    }
    
    /**
      * 将字符串转换为byte数组
      * @param s String
      * @return byte[]
      */
    public static byte[] stringToBytes(String s) {
        return s.getBytes();
    }
    
    /**
      * 将高字节数组转换为int
      * @param b byte[]
      * @return int
      */
    public static int hBytesToInt(byte[] b) {
        int s = 0;
        for (int i = 0; i < 3; i++) {
            if (b >= 0) {
                s = s + b;
            } else {
                s = s + 256 + b;
            }
            s = s * 256;
        }
        if (b[3] >= 0) {
            s = s + b[3];
        } else {
            s = s + 256 + b[3];
        }
        return s;
    }
    
    /**
      * 将低字节数组转换为int
      * @param b byte[]
      * @return int
      */
    public static int lBytesToInt(byte[] b) {
        int s = 0;
        for (int i = 0; i < 3; i++) {
            if (b[3-i] >= 0) {
                s = s + b[3-i];
            } else {
                s = s + 256 + b[3-i];
            }
            s = s * 256;
        }
        if (b[0] >= 0) {
            s = s + b[0];
        } else {
            s = s + 256 + b[0];
        }
        return s;
    }
    
    
    /**
      * 高字节数组到short的转换
      * @param b byte[]
      * @return short
      */
    public static short hBytesToShort(byte[] b) {
        int s = 0;
        if (b[0] >= 0) {
            s = s + b[0];
        } else {
            s = s + 256 + b[0];
        }
        s = s * 256;
        if (b[1] >= 0) {
            s = s + b[1];
        } else {
            s = s + 256 + b[1];
        }
        short result = (short)s;
        return result;
    }
    
    /**
      * 低字节数组到short的转换
      * @param b byte[]
      * @return short
      */
    public static short lBytesToShort(byte[] b) {
        int s = 0;
        if (b[1] >= 0) {
            s = s + b[1];
        } else {
            s = s + 256 + b[1];
        }
        s = s * 256;
        if (b[0] >= 0) {
            s = s + b[0];
        } else {
            s = s + 256 + b[0];
        }
        short result = (short)s;
        return result;
    }
    
    /**
      * 高字节数组转换为float
      * @param b byte[]
      * @return float
      */
    public static float hBytesToFloat(byte[] b) {
        int i = 0;
        Float F = new Float(0.0);
        i = ((((b[0]&0xff)<<8 | (b[1]&0xff))<<8) | (b[2]&0xff))<<8 | (b[3]&0xff);
        return F.intBitsToFloat(i);
    }
    
    /**
      * 低字节数组转换为float
      * @param b byte[]
      * @return float
      */
    public static float lBytesToFloat(byte[] b) {
        int i = 0;
        Float F = new Float(0.0);
        i = ((((b[3]&0xff)<<8 | (b[2]&0xff))<<8) | (b[1]&0xff))<<8 | (b[0]&0xff);
        return F.intBitsToFloat(i);
    }
    
    /**
      * 将byte数组中的元素倒序排列
      */
    public static byte[] bytesReverseOrder(byte[] b) {
        int length = b.length;
        byte[] result = new byte[length];
        for(int i=0; i<length; i++) {
            result[length-i-1] = b;
        }
        return result;
    }
    
    /**
      * 打印byte数组
      */
    public static void printBytes(byte[] bb) {
        int length = bb.length;
        for (int i=0; i<length; i++) {
            System.out.print(bb + " ");
        }
        System.out.println("");
    }
    
    public static void logBytes(byte[] bb) {
        int length = bb.length;
        String ut = "";
        for (int i=0; i<length; i++) {
            ut = out + bb + " ";
        }
    
    }
    
    /**
      * 将int类型的值转换为字节序颠倒过来对应的int值
      * @param i int
      * @return int
      */
    public static int reverseInt(int i) {
        int result = FormatTransfer.hBytesToInt(FormatTransfer.toLH(i));
        return result;
    }
    
    /**
      * 将short类型的值转换为字节序颠倒过来对应的short值
      * @param s short
      * @return short
      */
    public static short reverseShort(short s) {
        short result = FormatTransfer.hBytesToShort(FormatTransfer.toLH(s));
        return result;
    }
    
    /**
      * 将float类型的值转换为字节序颠倒过来对应的float值
      * @param f float
      * @return float
      */
    public static float reverseFloat(float f) {
        float result = FormatTransfer.hBytesToFloat(FormatTransfer.toLH(f));
        return result;
     }
    
    }

     

     

     

     

     

     

     

     

     

     

     

     

     

     

                       

     

     

     

        

     

    展开全文
  • 网络字节序、主机字节序

    千次阅读 2015-12-11 16:34:01
    使用TCP/IP协议进行网络应用开发的朋友首先... 2) 网络地址(32位无符号整形,网络字节序,大头)  3)主机地址 (主机字节序)  IP地址是IP网络中数据传输的依据,它标识了IP网络中的一个连接,一台主机可以有多个

    使用TCP/IP协议进行网络应用开发的朋友首先要面对的就是对IP地址信息的处理。IP地址其实有三种不同的表示格式:

           1)Ascii(网络点分字符串)-

           2) 网络地址(32位无符号整形,网络字节序,大头)

           3)主机地址 (主机字节序)  

           IP地址是IP网络中数据传输的依据,它标识了IP网络中的一个连接,一台主机可以有多个IP地址,IP分组中的IP地址在网络传输中将保持不变。下面具体介绍IP地址的三种不同表示格式。

    IP地址是IP网络中数据传输的依据,它标识了IP网络中的一个连接,一台主机可以有多个IP地址,IP分组中的IP地址在网络传输中将保持不变。下面具体介绍IP地址的三种不同表示格式。

    一、点分10进制表示格式   


            这是我们最常见的表示格式,比如某机的IP地址可能为“202.101.105.66”。事实上,对于Ipv4(IP版本)来说,IP地址是由一个32位的二进制数所构成,但这样一串数字序列无疑是十分冗长并且难以阅读和记忆的。为了方便人们的记忆和使用,就将这串数字序列分成4组,每组8位,并改为用 10进制数进行表示,最后用小原点隔开,于是就演变成了“点分10进制表示格式”。   
           来看看刚才那个IP地址的具体转化过程:   
           IP实际地址:11001010011001010110100101000010   
          分成4组后:    11001010   01100101   01101001   01000010   
          十进制表示:   202                  101              105                66   

          点分表示:       202.101.105.66   

    二、网络字节顺序格式(NBO,Network   Byte   Order)  

           网络字节顺序格式和主机字节顺序格式一样,都只在进行网络开发中才会遇到。因此,在下面的介绍中,我假设读者对Socket编程知识有一定的基础。   
          在网络传输中,TCP/IP协议在保存IP地址这个32位二进制数时,协议规定采用在低位存储地址中包含数据的高位字节的存储顺序(大头),这种顺序格式就被称为网络字节顺序格式。在实际网络传输时,数据按照每32位二进制数为一组进行传输,由于存储顺序的影响,实际的字节传输顺序是由高位字节到低位字节的传输顺序。   为了使通信的双方都能够理解数据分组所携带的源地址、目的地址以及分组的长度等二进制信息,无论是主机还是路由器,在发送每一个分组以前,都必须将二进制信息转换为TCP/IP标准的网络字节顺序格式。网络字节顺序格式的地址不受主机、路由器类型的影响,它的表示是唯一的。   

    inet_ntop()代替inet_ntoa(),这个是更通用的函数,可以处理ipv4和ipv6地址到字符串的转换,而inet_ntoa只可以处理ipv4

    在Socket编程开发中,通过函数inet_addr和inet_ntoa可以实现点分字符串与网络字节顺序格式IP地址之间的转换。   
      inet_addr函数原型如下:   
    1. unsigned   long   inet_addr(const   char   FAR   *   cp)     
      函数中的参数cp指向网络中标准的点分地址字符串,其中每个以点分开的数字不可以大于255,这些数字可以是十进制、八进制、十六进制或者混合使用。如 “10.23.2.3”、“012.003.002.024”、“0xa.0x3.0x14.0x2”、“10.003.2.0x12”。   

     我们在前面的socket编程提到client端的代码,连接本地端口:

    1. /* File Name: client.c */    
    2.   
    3. #include<stdio.h>  
    4. #include<stdlib.h>  
    5. #include<string.h>  
    6. #include<errno.h>  
    7. #include<sys/types.h>  
    8. #include<sys/socket.h>  
    9. #include<netinet/in.h>  
    10. #define MAXLINE 4096  
    11.   
    12. int main(int argc, char** argv)  
    13. {  
    14.     int    sockfd, n,rec_len;  
    15.     char    recvline[4096], sendline[4096];  
    16.     char    buf[MAXLINE];  
    17.     struct sockaddr_in    servaddr;  
    18.   
    19.     if( (sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0){  
    20.         printf("create socket error: %s(errno: %d)\n", strerror(errno),errno);  
    21.         exit(0);  
    22.     }  
    23.   
    24.     memset(&servaddr, 0, sizeof(servaddr));  
    25.     servaddr.sin_family = AF_INET;  
    26.     servaddr.sin_port = htons(8000);  
    27.     //可以使用:inet_pton(AF_INET,  "127.0.0.1", servaddr.sin_addr);  
    28.     servaddr.sin_addr.s_addr = inet_addr("127.0.0.1");//将字符串形式的IP地址转换为按网络字节顺序的整形值  
    29.     connect(sockfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) ;  
    30.     printf("send msg to server: \n");  
    31.     fgets(sendline, 4096, stdin);  
    32.     send(sockfd, sendline, strlen(sendline));  
    33.     irec_len = recv(sockfd, buf, MAXLINE,0);  
    34.        
    35.     buf[rec_len]  = '\0';  
    36.     printf("Received : %s ",buf);  
    37.     close(sockfd);  
    38. }  

    三、主机字节顺序格式(HBO,Host   Byte   Order)    

            主机字节顺序格式顾名思义,其IP地址的格式是和具体主机或者路由器相关的。对于不同的主机,在进行IP地址的存储时有不同的格式,比如对于 Motorola   68k系列主机,其HBO与NBO是相同的。而对于Intel   x86系列,HBO与NBO则正好相反。   
            在Socket编程中,有四个函数来完成主机字节顺序格式和网络字节顺序格式之间的转换,它们是:htonl、htons、ntohl、和ntohs。 htons和ntohs完成16位无符号数的相互转换,htonl和ntohl完成32位无符号数的相互转换。   
            在实际应用中我们常见到将端口号转换的例子(如上例)。这是因为,如果用户输入一个数字,而且将指定使用这一数字作为端口号,应用程序则必须在使用它建立地址以前,把它从主机字节顺序转换成网络字节顺序(使用htons()函数),以遵守TCP/IP协议规定的存储标准。相应地,如果应用程序希望显示包含于某一地址中的端口号(例如从getpeername()函数中返回的),这一端口号就必须在被显示前从网络顺序转换到主机顺序(使用ntohs()函数)。   
            那么,对于IP地址,主机字节顺序格式的转换又有哪些应用呢?   
            应用一,如果想知道从202.156.2.23到202.156.9.65这两个IP之间到底有多少个主机地址怎么办?这时就可以将两个IP地址转换为主机字节顺序的格式然后相减来得到,具体的实现如下:   
    1. int   GetIPCount(char   *   ip1,char   *   ip2)   {     
    2.     long   pp;;     
    3.     long   ss;;     
    4.       
    5.     pp   =   ntohl(inet_addr(ip1));;     
    6.     ss   =   ntohl(inet_addr(ip2));;     
    7.       
    8.     return(ss   -   pp   +   1);;     
    9. }   
            应用二,如果对一个网段进行扫描,比如,当前正在扫描202.156.23.255,怎么让程序知道下一个应扫的IP是202.156.24.0?这时可以将当前IP转换成主机字节顺序格式并加1后,在转换回网络格式
    即可,具体实现如下:   
    1. char   *   GetNextIp(char   *   m_curip)   {     
    2.       struct   sockaddr_in   in;;     
    3.       long   pp;;     
    4.       char   *   re;;     
    5.     
    6.       pp   =   ntohl(inet_addr(m_curip));;     
    7.       pp   =   pp   +   1;;     
    8.     
    9.       in.sin_addr.s_addr   =   htonl(pp);;     
    10.       re   =   inet_ntoa(in.sin_addr);;     
    11.   
    12.       return   (re);;     
    13. }     

          总结   

           介绍了IP地址的三种不同表示格式,包括各种格式产生的原因、具体含义以及在Socket编程开发中的一些应用。在实际应用中,必须遵循应用时所应采用的格式标准,同时还应灵活运用格式间的相互转换以及计算技巧。

    1)字节序

    字节序又称端序,尾序,英文:Endianness。在计算机科学领域中,字节序是指存放多字节数据的字节(byte)的顺序,典型的情况是整数在内存中的存放方式和网络传输传输顺序。Endianness有时候也可以用指位序(bit)。

            一般而言,字节序指示了一个UCS-2字符的哪个字节存储在低地址。如果LSByte 在MSByte的前面,即LSB为低地址,则该字节序是小端序;反之则是大端序。在网络编程中,字节序是一个必须被考虑的因素,因为不同的处理器体系可能采用不同的字节序。在多平台的代码编程中,字节序可能会导致难以察觉的bug。

    如long型数据的地址是ox001, ox002, ox003,ox004。则该数据的内存地址是ox001。

    在不同的处理器的存放多字节数据的方式主要有两种:

    大端序(英文名称为big endian)指从最高位起存,位数最大的数字在最前,即高字节存于内存低地址,低字节存于内存高地址, 从最高有效字节到最低有效字节的顺序存储对象。

    小端序(英文名称为little endian)指从对低位起存,位数最小的数字在最前。 即低字节存于内存低地址,高字节存于内存高地址,从最低有效字节到最高有效字节的顺序存储对象。

    简单打个比方说,十进制数12345。1的位数最高,是万位;5的位数最低,是个位。 

    大端序的话,就是从万位开始存,表示为12345; 
    小端序的话,就是从各位开始存,表示为54321.

    再如一个long型数据0x12345678的存储表示:

    大端序存储表示:

     

    内存地址

    数据

    内存低地址-->    a

    0x001

    12

    a+1

    0x002

    34

    a+2

    0x003

    56

    内存高地址-->a+3

    0x004

    78

    小端序的存储表示:

     

    内存地址

    数据

    内存低地址-->a

    0x001

    78

    a+1

    0x002

    56

    a+2

    0x003

    34

    内存高地址-->a+3

    0x004

    12


    2)网络序

    网络传输一般采用大端序,也被称之为网络字节序,或网络序IP协议中定义大端序为网络字节序。

    socketAPI定义了一组转换函数,用于16和32bit整数在网络序和本机字节序之间的转换。htonl,htons用于本机序转换到网络序;ntohl,ntohs用于网络序转换到本机序。


    展开全文
  • 不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序最常见的有两种1. Little endian:将低序字节存储在起始地址2. Big endian:将高序字节存储在起始地址LE little-endian最符合人...

    不同的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.

    注:

    1、网络与主机字节转换函数:htons ntohs htonl ntohl (s 就是short l是long h是host n是network)

    2、不同的CPU上运行不同的操作系统,字节序也是不同的,参见下表。

    处理器    操作系统    字节排序

    Alpha    全部    Little endian

    HP-PA    NT    Little endian

    HP-PA    UNIX    Big endian

    Intelx86    全部    Little endian

    Motorola680x()    全部    Big endian

    MIPS    NT    Little endian

    MIPS    UNIX    Big endian

    PowerPC    NT    Little endian

    PowerPC    非NT    Big endian  

    RS/6000    UNIX    Big endian

    SPARC    UNIX    Big endian

    IXP1200 ARM核心    全部    Little endian

    谈到字节序的问

    题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用

    big endian方式存储数据,而x86系列则采用little endian方式存储数据。那么究竟什么是big endian,什么又是

    little endian呢?

    其实big endian是指低地址存放最高有效字节(MSB),而little endian则是低地址存放最低有效字节(LSB)。

    用文字说明可能比较抽象,下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:

    Big Endian

    低地址                              高地址

    ----------------------------------------->

    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    |     12     |      34    |     56      |     78    |

    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    Little Endian

    低地址                                    高地址

    ----------------------------------------->

    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    |     78     |      56    |     34      |     12    |

    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    从上面两图可以看出,采用big endian方式存储数据是符合我们人类的思维习惯的。而little endian,!@#$%^&*,见鬼去吧 -_-|||

    为什么要注意字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的

    存在。但是,如果你的程序要跟别人的程序产生交互呢?在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,

    而JAVA编写的程序则唯一采用big endian方式来存储数据。试想,如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时

    会产生什么结果?就拿上面的0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的指针传给了JAVA程序,由于

    JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。什么?竟然变成另外一个数字了?是的,就是这种后

    果。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。

    无独有偶,所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两

    台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。ANSI C中提供了下面四个转换字节序的宏。

    Intel 8080 和 Motorola 6800还有一个有趣的不同点:在两种微处理器中,LDA指令都是从一个特定的地址处装载到累加器。例如,在8080中,下列字节序列:

    cawk,20060920203337.gif

    将把存储在地址347Bh处的字节装载到累加器。现在把上述指令与6800的LDA指令相比较,后者采用称作6800的扩展地址模式:

    cawk,20060920203456.gif

    该字节序列把存储在地址7B34h处的字节装载到累加器A中。

    这种不同点是很微妙的。当然,你也可能认为它们的操作码不同:对8080来说是3Ah,对6800来说是B6h。但主要是这两种微处理器处理紧随

    操作码后的地址是不同的,8080认为低位在前,高位在后;6800则认为高位在前,低位在后。这种Intel和Motorola微处理器保存多字节数时

    的根本不同从没有得到解决。直到现在,Intel微处理器在保存多字节数时,仍是最低有效字节在前(即在最低存储地址处);而Motorola微处理器在

    保存多字节数时,仍是最高有效字节在前。

    这两种方法分别叫作little-endian(Intel方式)和big-endian(Motorola方式)。辩论这两种方式的优劣可能是

    很有趣的,不过在此之前,要知道术语big-endian来自于JonathanSwift的《Gulliver’sTravels》,指的是

    Lilliput和Blefuscu在每次吃鸡蛋前都要互相碰一下。这种辩论可能是无目的的。先不说哪种方法在本质上说是不是正确的,但这种差别的确当在

    基于little-endian和big-endian机器的系统之间共享信息时会带来附加的兼容性问题。

    展开全文
  • 网络字节序与主机字节序 不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序 最常见的有两种 1. Little endian:将低序字节存储在起始地址 2. Big endian:将高序字节存储在起始...
  • 不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序 最常见的有两种 1. Little endian:将低序字节存储在起始地址 2. Big endian:将高序字节存储在起始地址 LE little-...
  • 一、在进行网络通信时是否需要进行字节序转换? 相同字节序的平台在进行网络通信时可以不进行字节序转换,但是跨平台进行网络数据通信时必须进行字节序转换。 原因如下:网络协议规定接收到得第一个字节是高字节...
  • 总结:1、80X86使用小端法(即本地字节序),网络字节序使用大端法。  2、二进制的网络编程中,传送数据,最好以unsigned char, unsigned short, unsigned int  来处理, unsigned short ,unsigned short ...
  • 不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序 最常见的有两种 1. Little endian:将低序字节存储在起始地址 2. Big endian:将高序字节存储在起始地址 LE little
  • 一、在进行网络通信时是否需要进行字节序转换? 相同字节序的平台在进行网络通信时可以不进行字节序转换,但是跨平台进行网络数据通信时必须进行字节序转换。 原因如下:网络协议规定接收到得第一个字节是高字节...
  • 不同的CPU有不同的字节序类型 这些字节序是指整数在内存中保存的顺序 这个叫做主机序 最常见的有两种1. Little endian:将低序字节存储在起始地址2. Big endian:将高序字节存储在起始地址LE little-endian 最符合...
  • 下面我们就讲一讲,网络字节序的知识 大小端到底是什么? 大小端说的就是字节序,也就是一个数值需要多个字节的存储空间时,这多个字节存储在内存中的顺序。 系统向内存存储数值: 大端模式:将数值的高字节位...
  • 1.字节序(CPU对内存的访问顺序) ...网络字节序:规定网络当中传输的字节序使用大端,如果小端机器在传数据的时候,需要将数据转化为大端字节序进行传输,对端机器默认传输过来的数据是大端字节序 问.
  • 引言:在进行网络通信时是否需要进行字节序转换?  相同字节序的平台在进行网络通信时可以不进行字节序转换,但是跨平台进行网络数据通信时必须进行字节序转换。 原因如下:网络协议规定接收到得第一个字节是高...
  • 网络字节序故事的起源“endian”这个词出自《格列佛游记》。小人国的内战就源于吃鸡蛋时是究竟从大头(Big-Endian)敲开还是从小头(Little-Endian)敲开,由此曾发生过六次叛乱,其中一个皇帝送了命,另一个丢了...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,514
精华内容 605
关键字:

网络字节序、