概述
前言
一个内容为12(字符串)的文本文件,它的第一个字节是什么(小端序)?如果你的回答是0x32,那你真的应该好好理解下字节序了。如下图所示,我这里的正确答案是0x31。当然如果你的回答是不一定,这似乎也是对的。背后的原因比较复杂。
什么是字节序
首先,字节序又称端序,顾名思义“字节的顺序”。你可能会奇怪:字节还有顺序?让我们从生活中常见概念入手吧。
生活中的数
123456,如果让你把这串数读出来的话你肯定会说:“十二万三千四百五十六。”你瞧,从左往右,这就是我们读数字时的顺序。
那有没有从右往左看数字的时候呢?当然有!如果我让你计算123456+134659你肯定会先从个位算起(除非你想给自己找麻烦)。
把数存入内存
计算机不是神仙,它和你没有心灵感应。因此为了使CPU可以读取数据,我们要先把数据存入内存再让CPU读取。内存是按地址进行访问的存储器件。在内存中,每个字节都有它对应的地址。
那么,你要如何把数0x12345678存入内存呢?
一种存储方式如下所示。
当然你也可以选择另外一种存储方式。
以上两种方式没有对错之分,只要在存储和读取时保持一致即可。Intel公司的CPU普遍采用第二种方式。
字节序的小端序与大端序
- 像Intel这样把数的低字节存在内存低地址处的方案,我们称作小端序(Little-Endian),全称字节序的小端序,又称低(小)端字节序等。
- 反之,像第一种把数的高字节存在内存低地址处的方案,我们称作大端序(Big-Endian),全称字节序的大端序,又称高(大)端字节序等。
端序
你肯定要迫不及待地问,前文一直在说的端究竟是什么呢?顾名思义,端(end)就是一边、一头、一端的意思:
- 在内存中,我们以低地址为端;
- 网络传输中,我们以0时刻发送的数据为端。
因此字节序的大端序更精确的定义为:位置更高(重要,significant)的字节在端;相反,字节的小端序更精确的定义为位置更低(不重要)的字节在端。
大端序和小端序是两种不同的端序。现在Intel公司的CPU普遍采用字节小端序(字节序的小端序)。
因此下图以字节小端序读取的结果是0x78563412,以字节大端序读取的结果是0x12345678.
注意: 当数据按上图在内存中存储时,不存在(至少据我所知,不存在) 0x87654321或者0x21345678这样的读取顺序.这是因为内存是以字节为最小单位进行存取的,所以字节内比特(位)的顺序总是固定不变的。当数据在网络中传输时就会涉及到字节内位的顺序。
数据在网络中传输
网络字节序
根据RFC791,网络传输时(精确地说是使用IP协议进行网络传输时)以太网采用大端字节序。也就是说,如果你想发送0x12345678,那先发送0x12字节,最后发送0x78字节。
上个自然段的意思是这样的。CPU是个傻瓜,他不知道你要发送什么。因此要发送的内容你是最清楚的。这就是说,如果下图是按低端字节序存储的,这意味着你存储的数是0x78563412。那么你就应该先发送0x78这个字节。反之如果下图是按大端字节序存储的,那根据RFC791,你应该先发送0x12. C语言中htonl、ntohl这两个函数可以帮我们解决主机字节序与网络字节序之间的转换问题。
网络比特序
以太网是串行传输数据的,即一次只能发送一个比特位。这样就引出了一个问题:我们知道,要想传输0x1234,需要先发送0x12这个字节,可是对于0x12我们应该先发送哪一位呢?
0x12的二进制写法是00010010B,因此有两种发送顺序。即,按照时间顺序依次发送0-0-0-1-0-0-1-0,或者反过来,发送0-1-0-0-1-0-0-0.
至于以太网在硬件层面是否真的是严格串行传输的,我并不清楚。因为ISO/IEC/IEEE 8802-3:2021居然有5千多页,鄙人才疏学浅,找不出来。
网络以0时刻为端,在发送0-0-0-1-0-0-1-0时比特的高位(重要的那一位)在端,我们称之为大端比特序;反之发送0-1-0-0-1-0-0-0时比特的低位(不那么重要的位)在端,我们称之为小端比特序。现在网卡会自动帮我们处理网络传输中的比特序问题,因此网卡交给CPU的数据是以字节为最小单位的。
以太网的比特序是小端序。
考考你
为了确保你真的懂了,我们来举个例子。下图为TCP(RFC9293)数据报的格式。我们将Data称为“体”(身体的体),将Data上面的部分称为“头”(脑袋的意思)。
从字节序的角度
我要问的是:在以太网中发送数据时,是先发送头还是先发送体呢?
鉴于你可能没学过计算机网络,那么我来告诉你:头比体更加重要。因此,先发送头就代表更重要的字节在端,即上文中字节序的大端序的定义,反之则为字节序的小端序。前文说过,以太网采用字节序的大端序进行传输,因此会先发送头。在上面那副图中,各个字段会按照从左往右的顺序依次发送出去。
从比特序的角度
那么再来一个问题,假设Source Port的值是0x55AA。网卡会先发送哪个比特呢?
首先明确肯定会先发送0x55这个字节(高端字节序)。0x55=01010101B.以太网采用比特序的小端序,最右面的比特1最不重要,因此1在端,即先发送1.因此,字节0x55AA的发送顺序为1-0-1-0-1-0-1-0-0-1-0-1-0-1-0-1.
开头的问题
严格地说,文件中没有“大小端序”这个概念,不然,文件在不同端序CPU的系统上将无法通用,这肯定是我们不希望看到的。
在C#中,文件是按字节写入,因此不存在端序的问题——你想写什么就写什么。
在test1文件中,由于使用的是小端序CPU,变量的第一个字节自然是0x78,第二个字节是0x56……C#会直接把这个数组原封不动地写入文件。至于test2,字符1的utf-8编码是0x31,2是0x32……C#也会将他们原样写入文件。很多高级语言都是这样。
需要特别注意:在计算机中,字节序的概念被限制在CPU中,对于字节序的讨论应该和CPU息息相关。例如,汇编语言、操作系统等。 同理,关于比特序的讨论应该被限制在需要它的地方——网络的物理层/数据链路层的底部。像内存、硬盘这些设备只要符合规范,就可以在任何端序的CPU中使用。换句话说,它们本身都没有字节序的概念。
所以如果你拿着wireshark的截图问我:为啥在小端的CPU中出现了大端序,这个……我只能说它编程时就是这么设计的。毕竟这样你用起来更舒服。
后记
正如我反复强调的,CPU是个傻瓜,它并不清楚你的意图,只是按照你的指示做事。你要做什么,你自己是最清楚的。
好比我问你10100001B到底是有符号数还是无符号数,答案是:它既是无符号数161也是有符号数-95. 至于怎么解释完全取决于你自己。
书是从左往右读的,但古代的书都是从右往左读。这并不会影响书籍的内容,只要写和读的时候顺序一致即可。
最后
以上就是任性过客为你收集整理的什么是字节序(端序、低端字节序、高端字节序、网络字节序)前言什么是字节序数据在网络中传输考考你开头的问题后记的全部内容,希望文章能够帮你解决什么是字节序(端序、低端字节序、高端字节序、网络字节序)前言什么是字节序数据在网络中传输考考你开头的问题后记所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复