字节序 主机字节序与网络字节序互相转换

目录

1.什么是字节序?

2.字节序分类

3.为什么要有大端字节序和小端字节序?

4.主机字节序和网络字节序

5.主机字节序与网络字节序的转换

6.怎么判断自己的主机是小端字节序还是大端字节序呢?

7.模拟htonl、ntohl、htons、ntohs函数实现


1.什么是字节序?

字节序是处理器架构特性,用于指示像整数这样的大数据类型内部的字节如何排序。

简单来说,就是指超过一个字节的数据类型在内存中的存储的顺序。

那么很明显,像char这样的类型,肯定不存在字节序的问题了。

不同类型的处理器采用的字节序可能不同。见下表。

处理器处理器上运行的操作系统字节序
Alpha全部小端字节序
HP-PANT小端字节序
HP-PAUNIX大端字节序
Intel X86全部小端字节序
Motorola680x()全部大端字节序
MIPSNT小端字节序
MIPSUNIX大端字节序
PowerPCNT小端字节序
PowerPC非NT大端字节序
RS/6000UNIX大端字节序
SPARCUNIX大端字节序
IXP1200 ARM核心全部小端字节序

那么,上面提高的“大端字节序”、“小端字节序”到底是什么呢?请继续往下看吧!

2.字节序分类

大端字节序(big-endian):高位字节数据存放在低地址处,低位数据存放在高地址处;

小端字节序(little-endian):高位字节数据存放在高地址处,低位数据存放在低地址处。

以0x01234567为例,最高位为0x01,最低位为0x67,它的大端字节序和小端字节序的写法如下:

那么,为什么要有字节序呢?每次都要区分,多麻烦,统一成一种不是更方便吗?

3.为什么要有大端字节序和小端字节序?

计算机电路先处理低位字节,效率比较高,因为计算都是从低位开始的,所以计算机的内部处理大都是小端字节序。

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

4.主机字节序和网络字节序

前面说的字节序其实是指主机字节序,即主机字节序既包括小端字节序,又包括大端字节序。它与具体的CPU类型、操作系统类型等有关,就像前面的表格列出的。

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

网络字节序采用大端字节序

5.主机字节序与网络字节序的转换

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

(BSD Socket 是UNIX系统中通用的网络接口,它不仅支持各种不同的网络类型,而且也是一种内部进程之间的通信机制)

头文件:#include <arpa/inet.h>

uint32_t htonl(uint32_t hostlong);  //将一个无符号长整型数从主机字节序转换成网络字节序
uint16_t htons(uint16_t hostshort); //将一个无符号短整型数从主机字节序转换成网络字节序
uint32_t ntohl(uint32_t netlong);   //将一个无符号长整型数从网络字节序转换成主机字节序
uint16_t ntohs(uint16_t netshort);  //将一个无符号短整型数从网络字节序转换成主机字节序

注:

  1. n代表“network”---网络,h代表“host”---主机,l代表long的长度(32位),s代表short的长度(16位)
  2. 在使用小端字节序的系统中,也就是主机字节序和网络字节序不同,所以这些函数会把字节序进行转换
  3. 在使用大端字节序的系统中,也就是主机字节序和网络字节序相同,不需要转换,这些函数会定义成空宏

6.怎么判断自己的主机是小端字节序还是大端字节序呢?

最简单的,我们可以以int类型的1为例,1在内存中存储的大小端格式如下:

如果我们可以得到1在内存中存储的第一个字节,那么我们就可以知道当前系统是大端存储还是小端存储了。

如果第一个字节为1,就是小端字节序;

如果第一个字节为0,就是大端字节序。

测试代码如下:

#include <stdio.h>
int main()
{
    int a = 1;
    char pc = *(char*)(&a);
    if (pc == 1)
        printf("第一个字节为1,小端存储\n");
    else
        printf("第一个字节为0,大端存储\n");
 
    return 0;
}

7.模拟htonl、ntohl、htons、ntohs函数实现

定义数据类型:

typedef unsigned short int uint16;

typedef unsigned long int uint32;

短整型大小端互换:

#define BigLittleSwap16(A)  ((((uint16)(A) & 0xff00) >> 8) | (((uint16)(A) & 0x00ff) << 8))

长整型大小端互换:

#define BigLittleSwap32(A)  ((((uint32)(A) & 0xff000000) >> 24) | /

                             (((uint32)(A) & 0x00ff0000) >> 8) | /

                             (((uint32)(A) & 0x0000ff00) << 8) | /

                             (((uint32)(A) & 0x000000ff) << 24))

本机大端返回1,小端返回0:

int checkCPUendian()
{
       union{
              unsigned long int i;
              unsigned char s[4];
       }c;

       c.i = 0x12345678;
       return (0x12 == c.s[0]);
}

模拟htonl函数,将无符号长整型从本机字节序转换成网络字节序:

unsigned long int HtoNl(unsigned long int h)
{
       // 若本机为大端,与网络字节序同,直接返回
       // 若本机为小端,转换成大端再返回
       return checkCPUendian() ? h : BigLittleSwap32(h);
}

模拟htons函数,将无符号短整型从本机字节序转换成网络字节序:

unsigned short int HtoNs(unsigned short int h)
{
       // 若本机为大端,与网络字节序同,直接返回
       // 若本机为小端,转换成大端再返回
       return checkCPUendian() ? h : BigLittleSwap16(h);
}

模拟ntohl函数,将无符号长整型从网络字节序转换成本机字节序:

unsigned long int NtoHl(unsigned long int n)
{
       // 若本机为大端,与网络字节序同,直接返回
       // 若本机为小端,网络数据转换成小端再返回
       return checkCPUendian() ? n : BigLittleSwap32(n);
}

模拟ntohs函数,将无符号短整型从网络字节序转换成本机字节序:

unsigned short int NtoHs(unsigned short int n)
{
       // 若本机为大端,与网络字节序同,直接返回
       // 若本机为小端,网络数据转换成小端再返回
       return checkCPUendian() ? n : BigLittleSwap16(n);
}

 



参考:

理解字节序 大端字节序和小端字节序 

理解大小端字节序

主机字节序与网络字节序的转换函数:htonl、ntohl、htons、ntohs

理解字节序

网络字节序与主机字节序 和它们之间的相互转换函数

理解大小端字节序

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值