大端与小端概念、多字节之间与单字节多部分的大小端转换详解

  • 前言
  • 高字节、低字节
  • 高地址、低地址
  • 大端、小端
  • 网络字节序和主机字节序
  • 大小端转换用在什么地方
  • 特殊情况:关注每个字节具体数值,单字节多部分的大小端转换
  • websocket协议
  • ip协议
  • 多字节之间 常用大小端转换api


前言

本文主要介绍三个内容:

  1. 大端与小端概念
  2. 多字节之间的大小端转换
  3. 单字节多部分的大小端

    定义TCP/IP协议结构体的时候,常常搞不清楚一字节分成多个部分时,大小端是如何转换的,今天特意写下来,以免过几天又搞不清楚了。

    字节是计算机的基本单位,8bit=1byte,八位一字节,如果存储一个数,大于一个字节,由于计算机内存排布的不同,就要区分字节顺序:大端-Big Endian(High-byte first) 和 小端-Litter Endian(Low-byte first)

  本专栏知识点是通过零声教育的线上课学习,进行梳理总结写下文章,对c/c++linux课程感兴趣的读者,可以点击链接 C/C++后台高级服务器课程介绍 详细查看课程的服务。

高字节、低字节

一个int类型的整数:123456789

  • 最左边的叫高字节,即 0x07
  • 最右边的叫低字节,即 0x15

进制


二进制

00000111 01011011 11001101 00010101

十六进制

07 5B CD 15

高地址、低地址

    在内存中,多字节对象都是被存储为连续的字节序列。例如在C语言中,一个类型为int的变量n,如果其存储的首个字节的地址为0x1000,那么剩余3个字节的地址将存储在0x1001~0x1003。总之,不管具体字节顺序是以什么方式排列,内存地址的分配一般是从小到大的增长。我们常把0x1000称为低地址端,把0x1003称为高地址端。

java小端字节序转大端字节序 字节大小端转换_java小端字节序转大端字节序

大端、小端

  • 大端:高字节存放在低地址,低字节存放在高地址(大端从左往右,很符合人的思维)
  • 小端:低字节存放在低地址,高字节存放在高地址(低放低,大端的逆序)

(123456789)10 = (07 5B CD 15)16 ,这个int整形4字节该如何存储呢

内存地址(低——>高)

字节存储顺序

大端(Big Endian)

0x1000 0x1001 0x1002 0x1003

0x07 0x5B 0xCD 0x15

高字节存放在低地址

低字节存放在高地址

小端(Litter Endian)

0x1000 0x1001 0x1002 0x1003

0x15 0xCD 0x5B 0x07

低字节存放在低地址

高字节存放在高地址

java小端字节序转大端字节序 字节大小端转换_服务器_02

网络字节序和主机字节序

    网络字节序(Network Order):TCP/IP各层协议将字节序定义为大端(Big Endian),因此TCP/IP协议中使用的字节序通常称之为网络字节序。

    主机字节序(Host Order):整数在内存中保存的顺序,它遵循小端(Little Endian)规则(不一定,要看主机的CPU架构,不过大多数都是小端)。所以当两台主机之间要通过TCP/IP协议进行通信的时候就需要调用相应的函数进行主机序列(Little Endian)和网络序(Big Endian)的转换。

大小端转换用在什么地方

   从上面我们已经知道了,TCP/IP各层协议 一般都是大端 , 而我们常用的计算机,一般都是小端。

   所以如果我们要发送协议的时候,是要发送大端的;当我们接收到大端的协议,想要解析出来使用的时候,就要转成小端。

   再往小的看,我们转换,转换的是整数(short ,int ,uint等等),因为只有整数才会有多个字节,对于单字节的char来说,是不需要转换的(特殊情况见下)。

   如果是做跨平台开发时,双方需要协商好字节序,然后根据程序运行的环境,确定是否需要字节序转换。例如约定的通讯字节序为大端,默认的windows采用的小端,那收到数据后就需要做转换操作。

特殊情况:关注每个字节具体数值,单字节多部分的大小端转换

   关注某个字节的具体 bit 的时候,是需要考虑大小端的。我们上面所说的,都是多字节的情况,对于int来说,4字节,我们只关注字节与字节之间的顺序;而对于一个字节,并且我们把这8bit,分成多个部分的情况的时候,我们需要考虑 bit 与 bit 之间的顺序,是需要考虑大小端的。
   那么如何将大端的一字节的,转换成小端呢? 根据协议规定的字节内 每部分 的顺序,做 逆序 即可。

websocket协议

   举个例子,在websocket协议介绍与基于reactor模型的websocket服务器实现,里面,我们就根据websocket的协议,定义了结构体。

java小端字节序转大端字节序 字节大小端转换_运维_03


   在协议中,将一字节,分成了5个部分,我们常用的ntohs,ntohl都是针对多字节的,那么收到了这份协议,如何将这一字节的大端转换成小端呢?我们通过结构体来实现。当这一字节存入结构体内,我们通过下面的方法,变成小端。

typedef struct _ws_ophdr {
    unsigned char opcode: 4,
            rsv3: 1,
            rsv2: 1,
            rsv1: 1,
            fin: 1;
    unsigned char payload_len: 7,
            mask: 1;
} ws_ophdr;

ip协议

   在ip协议里面,我们观察到第一个字节中,version4个bit,hdrlen4个bit。但是这个大端,如果要转换成小端,就逆序即可。

java小端字节序转大端字节序 字节大小端转换_网络_04

struct iphdr {
    unsigned char hdrlen: 4,
            version: 4;
    unsigned char tos;
    unsigned short totlen;
    unsigned short id;
    unsigned short flag_offset;
    unsigned char ttl;
    unsigned char type;
    unsigned short check;
    unsigned int sip;
    unsigned int dip;
};

多字节之间 常用大小端转换api

  • 函数名的h表示主机host, n表示网络network, s表示short, l表示long;下面这几个函数, 如果本来不需要转换,函数内部就不会做转换。
#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);

IP地址转换函数:

   p->表示点分十进制的字符串形式
   to->到
   n->表示network网络

  • 将字符串形式的点分十进制IP转换为大端模式的网络IP(整形4字节数)
int inet_pton(int af, const char *src, void *dst);

  • 网络IP转换为字符串形式的点分十进制的IP
const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);