Python中套接字接收的惯用法

6 投票
3 回答
566 浏览
提问于 2025-04-16 14:07

我有一些使用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 个回答

0

ctypes模块可以为你在例子中使用的C类型uint16提供sizeof()的功能:

>>> import ctypes
>>> ctypes.sizeof(ctypes.c_uint16)
2
0

sys模块提供了一个叫做getsizeof()的函数,它可以告诉你一个对象占用多少字节的空间(这个函数是通过对象的__sizeof__方法来实现的)。如果你在使用自己定义的对象,最好仔细测试一下你的__sizeof__实现,不过对于标准类型来说,这个方法应该没问题。

另外,你也可以把数据转成picklejson格式,然后计算字符串的字符数,虽然这样可能会影响性能。

无论你用哪种方法,如果你要传输长度不固定的数据,首先要传送数据的大小,然后再根据这个大小来决定还需要读取多少数据。

其他注意事项:

  • 如果你还没看过,建议你去看看关于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
    >>>
5

你可以使用 struct 模块来把Python中的整数转换成字符串或者字节数组。只需要读取与类型头大小对应的字节数,然后用 struct 模块进行转换就可以了。 (注意:在编码和解码时,一定要使用正确的字节序标志)

撰写回答