网络字节序和大小端字节序

        先说说为什么会有大小端字节序的问题。现在PC机的一个整型变量一般是32位的,由4个字节组成。在计算机内存中,每个字节都是有地址的。也就是说一个整型的4个字节的地址是不同的,有高低地址之分。对于一个整数,如632523,其对应的二进制位1001 10100110 11001011。需要3个字节才能放得下。这时就存在一个问题,对于低8位11001011是存放在整型的那4个字节的低地址位还是高地址位。

        如果将低8位存放在4个字节中的低地址位,称为小端字节序,如果将低8位存放在高地址位,则为大端字节序。助记:沿着内存的增长方向,先存低8位是的小端;先存高8位的是大端。大小端字节序是由CPU决定的[1][2][3]。

        虽然不同的CPU厂商可以随意选择一种字节序作为自己的内存字节序,但是网络字节序就不能任由各个CPU选择,网络字节序被规定为大端字节序

        一般来说,主机要先把端口号从主机字节序转换到网络字节序。有下面的函数可以相互转换。

#include<netinet/in.h>
unsigned long int htonl(unsigned long int hostlong);
unsigned short int htons(unsigned short int hostshort);
unsigned long int ntohl(unsigned long int netlong);
unsigned short int ntohs(unsigned short int netshort);

        其中,htonl 表示”host to network long”。

        有了这些转换函数,用户就不需要知道所在的OS/CPU用的什么字节序。反正这些函数会被我们想要的值转换成网络字节序的值。


        除了这4个函数外,在头文件endian.h中,还定义了很多主机字节序和大小端字节序相互转换的函数。

        从这里可以看到,下面函数是在glibc 2.9及之后的版本才支持。可以用$ldd --version命令 查看glibc的版本。 

       网络字节序和大小端字节序_第1张图片

       其中,be是表示大端,le是表示小端。


       上图中的那些函数(其实那些是宏定义),用来配置端口号是没有用的,因为已经有htons函数了。但在解决TCP粘包问题中,这些函数就可以派上用场了。

        解决TCP粘包问题的一个方法是,在每条消息的头部添加一个长度字段。每一个主机在发送消息前,都把长度转换成大端字节序存放(转换成小端字节序也行,只要CS两端统一即可)。然后发送消息。对端在收到消息的长度字段后,就将其从大端字节序转换成主机字节序。

       这里存在一个问题,怎么将怎么将收到的字节流转换成大端字节序的整型变量。答案是用memcpy函数。在发送端将整型变量变成字节流也是用这个函数。

 

       如下面代码所示:

    //发送端
    int len = htobe32(a);
    char ch[10];
    memcpy(ch, &len, sizeof(len) );

    
    //接收端
    int len;
    char* message;
    memcpy(&len, message, sizeof(len) );
    a = be32toh(len);


参考:

[1]  http://en.wikipedia.org/wiki/Endianness

[2] http://stackoverflow.com/questions/9237317/what-makes-a-system-little-endian-or-big-endian

[3] http://superuser.com/questions/308274/what-determines-endianness



你可能感兴趣的:(字节序,大小端)