共计 4059 个字符,预计需要花费 11 分钟才能阅读完成。
1、引言
最近在从头重写 MobileIMSDK 的 TCP 版,自已组织 TCP 数据帧时就遇到了字节序大小端问题。所以,借这个机会独自整顿了这篇文章,心愿能加深大家对字节序问题的了解,增强对 IM 这种基于网络通信的程序在数据传输这一层的常识掌控状况。
程序员在写应用层程序时,个别不须要思考字节序问题,因为字节序跟操作系统和硬件环境无关,而咱们编写的程序要么不须要跨平台(比方只运行在 windows),要么须要跨平台时会由 Java 这种跨平台语言在虚拟机层屏蔽掉了。
但典型状况,当你编写网络通信程序,比方 IM 聊天利用时,就必须要思考字节序问题,因为你的数据在这样的场景下要跨机器、跨网络通信,必须解决不同零碎、不同平台的字节序问题。
* 浏览对象: 本文属于计算机基础知识,特地适宜从事网络编程方面工作(比方 IM 这类通信零碎)的程序员浏览。面视时,面视官个别都会聊到这个知识点。
本文已同步公布于“即时通讯技术圈”公众号,欢送关注:
▲ 本文在公众号上的链接是:点此进入,原文链接是:http://www.52im.net/thread-3101-1-1.html
2、什么是字节序?
字节序,是指数据在内存中的寄存程序,当字节数大于 1 时须要思考(只有一个字节的状况下,比方 char 类型,也就不存在程序问题啦)。
从下图中,能够直观的感触到什么是字节序问题:
(上图片改编自《C 语言打印数据的二进制格局 - 原理解析与编程实现》)
3、字节序的分类
字节序常被分为两类:
- 1)Big-Endian(大端字节序):高位字节排放在内存的低地址端,低位字节排放在内存的高地址端(这是人类读写数值的办法);
- 2)Little-Endian(小端字节序):低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。
举个具体的例子,0x1234567 的大端字节序和小端字节序写法如下:
如上图所示: 大端小端字节序最小单位 1 字节,即 8bit;大端字节序就是和咱们平时写法的程序一样,从低地址到高地址写入 0x01234567;而小端字节序就是和咱们平时的写法反过来,因为字节序最小单位为 1 字节,所以从低地址到高地址写入 0x67452301。
4、为什么会存在大端、小端字节序问题?
4.1 比拟正当的解释
一个比拟正当的解释是说: 计算机中电路优先解决低位字节,效率比拟高,因为计算机都是从低位开始的,所以计算机外部解决都是小端字节序。
而人类人类读写数值的办法,习惯用大端字节序,所以除了计算机的外部处,其余的场理合都是大端字节序,比方:网络传输和文件贮存时都是用的大端字节序(对于网络字节序,会在前面持续开展阐明)。
大小端字节序问题,最有可能是跟技术算硬件或软件的创造者们,在技术创建之初的一些技术条件或集体习惯无关。
所以大小端问题,体现在理论的计算机工业利用来上,不同的操作系统和不同的芯片类型可能都会有不同。
4.2 常见的操作系统和芯片应用的字节序
具体来说:DEC 和 Intel 的机器(X86 平台)个别采纳小端,IBM、Motorola(Power PC)、Sun 的机器个别采纳大端。
当然,这不代表所有状况。有的 CPU 即能工作于小端,又能工作于大端,比方:Arm、Alpha、摩托罗拉的 PowerPC。
而且,具体这类 CPU 是大端还是小端,和具体设置也无关。如:Power PC 反对小端字节序,但在默认配置时是大端字节序。
一般来说: 大部分用户的操作系统(如:Windows、FreeBsd、Linux)是小端字节序。少部分,如:Mac OS 是大端字节序。
4.3 如何判断用的是什么字节序?
怎么判断我的计算机里应用的是大端还是小端字节序呢?
上面的这段代码能够用来判断计算机是大端的还是小端。判断的思路是:确定一个多字节的值(上面应用的是 4 字节的整数),将其写入内存(即赋值给一个变量),而后用指针取其首地址所对应的字节(即低地址的一个字节),判断该字节寄存的是高位还是低位,高位阐明是 Big endian,低位阐明是 Little endian。
include <stdio.h>
int main ()
{
unsigned int x = 0x12345678;
char*c = (char*)&x;
if(*c == 0x78) {
printf(“Little endian”);
} else{
printf(“Big endian”);
}
return 0;
}
5、“大端”、“小端”名字由来
依据网上的材料,据说名字的由来跟乔纳森·斯威夫特的驰名讥刺小说《格列佛游记》无关。
书中的故事是这样的: 一般来说,大家都认为吃鸡蛋前,原始的办法是突破鸡蛋较大的一端。可是当今皇帝的祖父小时候吃鸡蛋,一次按古法打鸡蛋时碰巧将一个手指弄破了,因而他的父亲,过后的皇帝,就下了一道敕令,命令整体臣民吃鸡蛋时突破鸡蛋较小的一端,违令者重罚。
君子国内部决裂成 Big-endian 和 Little-endian 两派,区别在于一派要求从鸡蛋的大头把鸡蛋突破,另一派要求从鸡蛋的小头把鸡蛋突破。
小人国国王扭转了关上鸡蛋的方位与理由,并由此导致了批改法律、引发和平和宗教改革等一序列事件的产生。
《格列佛游记》中的这则故事,本来是借以讥刺英国的政党之争。而在计算机工业中,也借用了这个故事来代指大家在数据贮存字节程序中的一致,并把“大端”(Big-endian)、“小端”(Little-endian)的名字,沿用到了计算机中。
(上图片改编自《“字节序”是个什么鬼?》)
或者,借用这个故事来命名大小端字节序问题,无非就是想通知大家,所谓的“大端”、“小端”实际上可能无关计算机性能,更多的只是创造者们在创建计算机之初,代入了集体的一些约定俗成的习惯而已。
6、什么是网络字节序?
6.1 字节序问题给网络通信带来的困扰
对于搞网络通信利用(比方 IM、音讯推送、实时音视频)开发的程序员来说,自已写通信底层的话是肯定会遇到大小端问题的,对于网络字节序这个知识点是肯定要必知必会。(当然,你要是很没谋求的认为,反正我公司就让租租第 3 方,能用就行,具体通底层怎么写我才不想掉头发去思考那么多。。。。那哥也救不了你。。)
下面所说的大小端字节序都是在说计算机本人,也被称作主机字节序。同型号计算机上写的程序,在雷同的零碎下面运行总归是没有问题。
但计算机网络的呈现让大小端问题变的复杂化了,因为每个计算机都有本人的主机字节序。不同计算机之间通过网络通信时:我“说”的你听不懂,你“说”我也听不懂,这可怎么办?
6.2 TCP/IP 协定强行约定了字节序计划
好消息是,TCP/IP 协定很好的解决了这个问题,TCP/IP 协定规定应用“大端”字节序作为网络字节序。
这样,即便不应用大端的计算机也没有关系,因为发送数据的时候能够将本人的主机字节序转换为网络字节序(即“大端”字节序),对接管到的数据转换为本人的主机字节序。这样一来,也就达到了与 CPU、操作系统无关,实现了网络通信的标准化。
具体的原理就是:
- 1)TCP/IP 协定会把接管到的第一个字节当作高位字节对待,这就要求发送端发送的第一个字节是高位字节;
- 2)而在发送端发送数据时,发送的第一个字节是该数值在内存中的起始地址处对应的那个字节。
也就是说,该数值在内存中的起始地址处对应的那个字节就是要发送的第一个高位字节(即:高位字节寄存在低地址处)。由此可见,多字节数值在发送之前,在内存中就是以大端法寄存的。
所以说,网络字节序就是大端字节序。
6.3 主机字机序到网络字节序的转换
那么,为了程序的兼容,程序员们每次发送和承受数据都要进行转换,这样做的目标是保障代码在任何计算机上执行时都能达到预期的成果。
通信时的这种罕用的操作,Socket API 这一层,个别都提供了封装好的转换函数,不便程序员应用。比方从主机字节序到网络字节序的转换函数:htons、htonl(C 语言中罕用),从网络字节序到主机字节序的转换函数:ntohs、ntohl(C 语言中罕用)。当然,也能够编写本人的转换函数。
7、实际中的大小端字节序解决
在我编写 MobileIMSDK 的 TCP 版时(MobileIMSDK 是我开源的 IM 通信层库),同样遇到了大小端字节序问题。
以 MobileIMSDK 的 iOS 端拼装网络数据收发的代码为例:
如上图代码所示,留神以下两个大小端转换函数的应用:
- 1)第 27 行“CFSwapInt32HostToBig”函数:网络收回数据之前,先将主机字节序转为网络字节序(即大端字节序);
- 2)第 53 行“CFSwapInt32BigToHost”函数:收到原始网络数据后,转为主机字节序后就能够在程序中失常应用了。
如果对网络大小端转换这方面的实际感兴趣,能够自已去下载 MobileIMSDK 源码试一试:https://github.com/JackJiang2011/MobileIMSDK。
8、参考资料
[1]“字节序”是个什么鬼?
[2] 大小端及网络字节序
[3] C 语言打印数据的二进制格局 - 原理解析与编程实现
附录:系列文章
本文是系列文章中的第 9 篇,本系列纲要如下:
《脑残式网络编程入门 (一):跟着动画来学 TCP 三次握手和四次挥手》
《脑残式网络编程入门 (二):咱们在读写 Socket 时,到底在读写什么?》
《脑残式网络编程入门 (三):HTTP 协定必知必会的一些常识》
《脑残式网络编程入门 (四):疾速了解 HTTP/ 2 的服务器推送 (Server Push)》
《脑残式网络编程入门 (五):每天都在用的 Ping 命令,它到底是什么?》
《脑残式网络编程入门 (六):什么是公网 IP 和内网 IP?NAT 转换又是什么鬼?》
《脑残式网络编程入门 (七):面视必备,史上最艰深计算机网络分层详解》
《脑残式网络编程入门 (八):你真的理解 127.0.0.1 和 0.0.0.0 的区别?》
《脑残式网络编程入门 (九):面试必考,史上最艰深大小端字节序详解》(本文)
(本文同步公布于:http://www.52im.net/thread-3101-1-1.html)