Python中套接字接收的惯用法
我有一些使用C语言的伯克利套接字API进行套接字编程的经验。一般来说,任何套接字编程都需要一种策略,让接收方的套接字知道它应该接收多少数据。这可以通过使用头部长度字段或者分隔符字符来实现。通常,我更喜欢使用一个包含长度的头部字段。
当然,我们还需要知道长度头部字段本身的大小,这个大小是一个固定的值,发送方和接收方必须达成一致。在C语言中,这很容易实现,因为原生的整数类型大小是固定的,并且是二进制格式,所以你可以简单地写一些类似这样的代码:
uint16_t bytes_to_receive;
recv(sock, &bytes_to_receive, sizeof(bytes_to_receive), 0);
bytes_to_receive = ntohs(bytes_to_receive);
// Now receive 'bytes_to_receive' bytes...
但是在Python的套接字编程中,如何实现这种方式呢?在Python中,整数是对象,而被序列化的整数是可变长度的字节数组。所以我们不能把被序列化的整数当作长度头部字段,因为我们无法确定它的字节大小。
当然,我可以发送一个已知大小的字节数组,里面包含一个二进制整数,比如 b'\x05\x00'
,这样可以创建一个值为5的16位二进制整数,采用小端格式,但这似乎并不是正确的方法。
那么,在Python中通常是怎么做到的呢?
3 个回答
ctypes模块可以为你在例子中使用的C类型uint16
提供sizeof()
的功能:
>>> import ctypes
>>> ctypes.sizeof(ctypes.c_uint16)
2
sys
模块提供了一个叫做getsizeof()
的函数,它可以告诉你一个对象占用多少字节的空间(这个函数是通过对象的__sizeof__
方法来实现的)。如果你在使用自己定义的对象,最好仔细测试一下你的__sizeof__
实现,不过对于标准类型来说,这个方法应该没问题。
另外,你也可以把数据转成pickle
或json
格式,然后计算字符串的字符数,虽然这样可能会影响性能。
无论你用哪种方法,如果你要传输长度不固定的数据,首先要传送数据的大小,然后再根据这个大小来决定还需要读取多少数据。
其他注意事项:
- 如果你还没看过,建议你去看看关于sockets的API文档。
- 要注意,像列表这样的复合类型需要额外的空间,所以:
>>> import sys >>> a = [1,3,4] >>> sys.getsizeof(a) 96 >>> l = 0 >>> for i in a: ... l += sys.getsizeof(i) ... >>> print l 72 >>>
你可以使用 struct
模块来把Python中的整数转换成字符串或者字节数组。只需要读取与类型头大小对应的字节数,然后用 struct
模块进行转换就可以了。 (注意:在编码和解码时,一定要使用正确的字节序标志)