0
点赞
收藏
分享

微信扫一扫

主机字节序和网络字节序

  • 英文小册原文地址:​​beej.us/guide/bgnet…​​
  • 作者:Beej
  • 中文翻译地址:​​www.chanmufeng.com/posts/netwo…​​

现将目录贴下:

  • ​​什么是socket​​
  • ​​两种Socket​​
  • ​​漫谈网络​​
  • ​​IP地址、struct以及地址转换​​
  • ​​IP地址,IPv4和IPv6​​
  • ​​子网​​
  • ​​字节序​​
  • struct结构
  • 再谈IP地址
  • 从IPv4迁移到IPv6
  • IP地址,IPv4和IPv6
  • 字节序
  • struct结构
  • 再谈IP地址
  • System call 或 Bust
  • getaddrinfo()—准备开始!
  • socket()—拿到文件描述符!
  • bind()—我在监听哪个端口?
  • connect()—嘿,你好啊!
  • listen()—会有人联系我吗?
  • accept()—感谢呼叫3490端口
  • send() and recv()—跟我唠唠吧,宝儿!
  • sendto() and recvfrom()—用DGRAM风格跟我说话
  • close() and shutdown()—滚犊子!
  • getpeername()—你哪位?
  • gethostname()—我是谁?
  • Client-Server基础
  • 一个简单的流服务器
  • 一个简单的流客户端
  • Datagram Sockets
  • 技术进阶
  • Blocking—何谓阻塞?
  • poll()—同步的I/O多路复用
  • select()—老古董的同步I/O多路复用
  • 数据只传了一部分怎么办?
  • Serialization-如何封装数据?
  • 数据封装
  • 广播数据包-大声说「Hello,World」

什么是字节序

字节是内存读写的最小单位,一个字节能够表示的数据范围是0~255,也就是说如果你要保存一个在此范围区间的数字,一个字节足矣。

但是像占4个字节的​​int​​​,8个字节的​​double​​​,该怎么存储呢?多个字节该用怎样的顺序来进行排列?举个例子,​​0xb3f4​​​,数值的高位​​b3​​是存储在了内存的高地址处,还是内存的低地址处呢?

这种字节的排列顺序就叫做字节序,字节序有两种:

  1. 大端字节序

数值的低字节放在内存的低地址处,数值的高字节放在内存的高地址。

  1. 小端字节序

数值的低字节放在内存的高地址处,数值的高字节放在内存的低地址。

主机字节序

当我们谈论字节序,大部分时候对应的都是CPU访问内存时的概念,比如对下图而言:

主机字节序和网络字节序_掘金·日新计划

内存低位存储的字节是​​0xb3​​​,高位存储的是​​0xf4​​​,至于CPU读取这个2字节数据的时候,是将其解释为​​0xb3f4​​​(小端)还是​​0xf4b3​​(大端)就取决于CPU采用的是哪一种字节序了。

常用的CPU字节序如下:

  • 大端字节序:IBM、PowerPC
  • 小端字节序:x86

这种CPU字节序也被称为主机字节序(Host Byte Order) 。翻译一下就是,IBM的主机序是大端,x86的主机序是小端。

问题来了。

世界上的主机这么多,每台主机的CPU类型还不一样,A主机按照A的主机字节序发信息给B主机,B主机如果直接按照B的主机字节序来解析信息,那么极有可能会产生错误。

要想解决这个问题,还必须引出一些其他情况下的字节序。

为了避免引起混淆,我在这里强调一下,字节序就分为2种,大端和小端。而所谓的主机字节序以及下面我将提到的两种,都只是字节序作用在不同的场景中取得特定名称罢了。

文件存储字节序

顾名思义,就是存储文件信息的时候用的是大端还是小端。

bmp格式的图片属于小端字节序,jpeg格式的图片就是大端字节序。

这里提及文件存储字节序主要是为了让大家理解一个事实:采用什么字节序完全是开发者设计产品时的一种技术选型罢了。但是这种选型一定要成为一种标准,让其他开发者解析的时候明白应该用哪种字节序,否则jpeg的图片也就没办法在所有的电脑上正常显示了,你说对吗?

现在好了,所有jpeg软件的开发者都知道应该用大端字节序来保存jpeg图片,但是对于从其他主机传过来的jpeg数据流,开发者又怎么知道这个数据流用的是大端还是小端呢?

这就是网络字节序(Network Byte Order) 的问题了。

网络字节序

我想你应该能想明白了,网络字节序不是大端就是小端,不可能存在「可能是大端也可能是小端」这种情况,否则网络传递的消息全都乱套了!

TCP/IP既然是一种标准,那标准自然就有自己的字节序规定。

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

也就是说,如果你想让你的消息能在其他设备正常解析,就必须按照大端字节序进行处理。比如,网络传输的是​​0x12345678​​​这个整形变量,首先被发送的应该是​​0x12​​​,接着是​​0x34​​​,然后是​​0x56​​​,最后是​​0x78​​。

字节序在socket中的例子

当你在网络封包或者在填充数据的时候,你需要确定你的端口号(port number)和ip地址都是符合网络字节序的。但是你并不知道你的主机字节序是大端还是小端,你自然也就搞不清你到底需不需要进行转换。

其实不必如此费神,不用在意主机序,直接调用函数强行将主机序转换为网络字节序即可。下面举一个C语言编写服务器代码绑定端口的例子。

主机字节序和网络字节序_ip地址_02

代码中被红色标记的部分就是分别将端口号​​33000​​由主机序转换为网络字节序,将ip地址由主机序转换为网络字节序的函数。

乍眼一看,函数名非常古怪,不好记忆,下面稍微解释一下,各位就豁然开朗了。

​Socket函数库​​​提供了这种字节序转换的函数,函数的作用对象有两种,分别是​​short​​​(2字节)和​​long​​​(4字节),这就是函数最后的「​​s​​​」和「​​l​​」的含义。

还有,主机字节序的英文是​​Host Byte Order​​​,简写为「​​h​​​」,网络字节序的英文是​​Network Byte Order​​​,简写为「​​n​​​」,「​​to​​​」表示转换,所以,如果你想将​​short​​​数据从主机字节序转换为网络字节序,那就应该是​​h-to-n-s​​​,表示​​Host to Network Short​​。是不是很简单?

所以理论上,你可以用「​​h​​​」、「​​to​​​」、「​​n​​​」、「​​s/l​​」这几个字母灵活组合,组成你想要的api,但是也别瞎搞,stolh()["Short to Long Host"]这种组合压根都不存在。

基本上你需要的也就是以下四种罢了:

htons() // host to network short
htonl() // host to network long
ntohs() // network to host short
ntohl() // network to host long

基本上,我们在进行socket编程时,在发送数据之前都需要把数据转换为网络字节序,并在收到数据之后再转换成主机字节序。

更多关于浮点数的处理,会在之后专门的章节提及。

如无特殊说明,本小册的数值预设都视为主机字节序。

举报

相关推荐

0 条评论