分享

long long 类型的网络字节顺序转换

 水底の小鱼 2009-05-31

做过socket的都知道网络字节转换的事情,网络中传输的数据是纯字节流,没有类型信息,从低地址开始传递;网络字节序通常为大端的,即先传递高字节,因此和大端的本地字节存储顺序一致,和小端的则截然相反。为了数据的一致性,就要把本地的数据转换成网络上使用的格式,然后发送出去,接收的时候也是一样的,经过转换然后才去使用这些数据。基本的库函数中提供了这样的可以进行字节转换的函数,如和htons( ) htonl( ) ntohs( ) ntohl( ),这里n表示networkh表示hosthtons( ) htonl( )用于本地字节向网络字节转换的场合,s表示short,即对2字节操作,l表示long即对4字节操作。同样ntohs( )ntohl( )用于网络字节向本地格式转换的场合。随着c99标准的推行,我们伟大的c中增加了新的类型long long int unsigned long long int,都是64位的,怎么办?不转肯定是不行,就得自己想办法把它转了。当然有很多方法,我这里想使用一种类比的解决方法,看看如何举一反三。

一、字节序定义字节序,顾名思义字节的顺序,再多说两句就是大于一个字节类型的数据在内存中的存放顺序。其实大部分人在实际的开发中都很少会直接和字节序打交道。唯有在跨平台以及网络程序中字节序才是一个应该被考虑的问题。一次Sun SPARCIntel X86的平台移植让我们的程序遭遇了“字节序问题”。

在所有的介绍字节序的文章中都会提到字节序分为两类:Big-EndianLittle-Endian。引用标准的Big-EndianLittle-Endian的定义如下:
a) Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
b) Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
c) 网络字节序:TCP/IP各层协议将字节序定义为Big-Endian,因此TCP/IP协议中使用的字节序通常称之为网络字节序。

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

-----------------------
未初始化的数据
----------------------         (统称数据段)
初始化的数据
-----------------------
正文段(代码段)
----------------------- 最低内存地址 0x00000000
以上图为例如果我们在栈上分配一个unsigned char buf[4],那么这个数组变量在栈上是如何布局的呢?看下图:
栈底(高地址)
----------
buf[3]
buf[2]
buf[1]
buf[0]
----------
栈顶(低地址)

现在我们弄清了高低地址,接着我来弄清高/低字节,如果我们有一个32位无符号整型0x12345678(呵呵,恰好是把上面的那4个字节buf看成一个整型),那么高位是什么,低位又是什么呢?其实很简单。在十进制中我们都说靠左边的是高位,靠右边的是低位,在其他进制也是如此。就拿0x12345678来说,从高位到低位的字节依次是0x120x340x560x78
高低地址和高低字节都弄清了。我们再来回顾一下Big-EndianLittle-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) -- 低位
---------------
栈顶(低地址)

在现有的平台上IntelX86采用的是Little-Endian,而像SunSPARC采用的就是Big-Endian

三、网络字节序的转换假设对于little endianIA-32架构上面的Linux,首先考虑网络字节转换的结果与原来有什么不同,如 int a = 0x12345678b = htnla),那么就应该是0x78563412。如果是 short c = 0x1234short d = 0x5678e = htons(c)f = htons(d),这样e=0x3412f=0x7856,如果能把ef调换一下组合放在一起,不就是一个整型aa=0x12345678)转换之后的值么?实验的代码如下:
#include <stdio.h>
struct ST{
    short val1;
    short val2;
};
union U{
    int val;
    struct ST st;
};

int main(void)
{
    int a = 0;
    union U u1, u2;

    a = 0x12345678;
    u1.val = a;
    printf("u1.val is 0x%x\n", u1.val);
    printf("val1 is 0x%x\n", u1.st.val1);
    printf("val2 is 0x%x\n", u1.st.val2);
    printf("after first convert is: 0x%x\n", htonl(u1.val));
    u2.st.val2 = htons(u1.st.val1);
    u2.st.val1 = htons(u1.st.val2);
    printf("after second convert is: 0x%x\n", u2.val);
    return 0;
}
输出结果:
u1.val is 0x12345678
val1 is 0x5678
val2 is 0x1234
after first convert is: 0x78563412
after second convert is: 0x78563412

按照这种想法我们实现long long int64bit)类型,把它分割成两个int32bit),然后分别使用htonl(),分别转换,然后再将两种int交换顺序重新组合,即实现了整个64位的八个字节的翻转。
代码如下:
#include <stdio.h>
struct ST{
    int val1;
    int val2;
};
union test {
    long long int val;
    struct ST st;
};

int main(void)
{
    long long int a;
    union test u1, u2;

    a = 0x7654321087654321LL;
    u1.val = a;
    u2.st.val2 = htonl(u1.st.val1);
    u2.st.val1 = htonl(u1.st.val2);
    printf("val1 is 0x%x\n", u2.st.val1);
    printf("val2 is 0x%x\n", u2.st.val2);
    printf("u1.val     is    : 0x%llx\n", u1.val);
    printf("after convert is : 0x%llx\n", u2.val);

    return 0;
}
执行结果:
val1 is 0x10325476
val2 is 0x21436587
u1.val     is    : 0x7654321087654321
after convert is  : 0x2143658710325476

另外注意long long int 最大值是0x7fffffffffffffff,即7后面15f263次方减1 unsigned long long int 最大值是0xffffffffffffffff16f(264次方减1)。程序中long long int可以简写为 long long,但是记住这是简写,就像longlong int的简写。

想看数据在内存中如何存储的,就用gdb吧!使用gdb x命令,如 x /xb &a表示要察看存储在变量a中的前一个字节(byte)中的数据(16进制)。x /xw &a 就是要察看变量a中前4个字节(word)数据(16进制)。x /xg &a 察看a开始8个字节的数据。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多