Python网络字节序和主机字节序

在C中关于网络字节序和主机字节序困扰了我一段时间,在Python中实现字节流的网络传输,必然这个问题也不可避免,但是我觉得在python中解决这个问题和演示起来比较方便一点。

上次用struct的pack方法实现了字节流(见 ),那么这里就牵涉到一个字节流的字节序问题,当一个16位的int型数据形成01的字节流时,是高位在前,还是低位在前?这里分为网络字节序,和主机字节序。

Little endian:将低序字节存储在起始地址

Big endian:将高序字节存储在起始地址

LE little-endian :
最符合人的思维的字节序,地址低位存储值的低位,地址高位存储值的高位,怎么讲是最符合人的思维的字节序,是因为从人的第一观感来说低位值小,就应该放在内存地址小的地方,也即内存地址低位反之,高位值就应该放在内存地址大的地方,也即内存地址高位

BE big-endian
  最直观的字节序,地址低位存储值的高位,地址高位存储值的低位,为什么说直观,不要考虑对应关系
只需要把内存地址从左到右按照由低到高的顺序写出,把值按照通常的高位到低位的顺序写出 两者对照,一个字节一个字节的填充进去

例子:在内存中双字0x01020304(DWORD)的存储方式

内存地址
4000 4001 4002 4003
LE 04 03 02 01
BE 01 02 03 04

例子:如果我们将0x1234abcd写入到以0x0000开始的内存中,则结果为
      big-endian  little-endian
0x0000  0x12      0xcd
0x0001  0x23      0xab
0x0002  0xab      0x34
0x0003  0xcd      0x12
x86系列CPU都是little-endian的字节序.

我们用python来试验一下:

>>> from struct import *

>>> pack('@h',14)

'\x0e\x00'

>>>
    int型的整数14用二进制表示应该为0000000000001110,十六进制的话应该为0x00\ox0e ,但是如果说是低位字节存放低位,那么低位字节0x0e就应该存在低位,然后高位就存放0x00,这就是我们电脑在内存中存放这个数的形式。我们用pack,参数为’@h’,就是把14按照主机字节序,以2字节的int型存放到内存中的。

网络字节顺序是TCP/IP中规定好的一种数据表示格式,它与具体的CPU类型、操作系统等无关,从而可以保证数据在不同主机之间传输时能够被正确解释。网络字节顺序采用big endian排序方式。那么如果我们要把数据流发到网络上,必须把字节顺序改为高字节在前,低字节在后,就是我们发送一个0000000000001110,我们必须先发高字节00000000,然后在发低字节00001110,也就是十六进制的0x00 0x0e

在struct的pack打包中,提供了一个’!’,来实现网络字节序,比如同样是上面的例子:

>>> from struct import *

>>> pack('!h',14)

'\x00\x0e'

>>>

可以看到,确实是高字节在前,低字节在后了。假如我们要把这个14通过UDP协议发送给另外一个主机,那么我们就要用这个字节序。

下面我看一个具体传输的例子吧,基于UDP的。

首先我们在server和client端都用网络字节序来pack和unpack,我们看看结果会是什么样:

Server端:

import socket

import struct

BUFSIZ=1024

ADDR=('localhost',2046)

recvsocket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

while True:

print 'waiting for the data'

data,addr=recvsocket.recvfrom(BUFSIZ)

print repr(data)

(data1,)=struct.unpack('h',data)

print repr(data1)

(data2,)=struct.unpack('!h',data)

print data2

recvsocket.close()

下面是client端:

import socket

import struct

BUFSIZ=1024

ADDR=('localhost',2046)

sendsocket=socket.socket(socket.AF_INET,socket.SOCK_DGRAM)

data=struct.pack('!h',14)

print repr(data)

sendsocket.sendto(data,ADDR)

sendsocket.close()

我们还是在client端往server端发一个14,int型数据,我们看看运行结果: 

Python网络字节序和主机字节序

Python网络字节序和主机字节序

其中红色命令窗口为client端,绿色是server端,可以看到14通过pack传出去的确是高字节在前,低字节在后,属于网络字节序,而server收到的14在内存中也是高字节在前,低字节在后的,这样我们通过unpack就能得到正确的结果。

整个过程是这样的:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/a01c4860cd29f26f8a139ed4fec3c1e2.html