首页 > 解决方案 > 如何可靠地从 TCP 套接字中准确读取 n 个字节?

问题描述

语境:

二进制协议通常定义给定大小的。该struct模块擅长解析,前提是所有内容都已在单个缓冲区中接收。

问题:

TCP 套接字是流。从套接字读取不能提供比请求更多的字节,但可以返回更少。所以这段代码不可靠:

def readnbytes(sock, n):
    return sock.recv(n)   # can return less than n bytes

天真的解决方法:

def readnbytes(sock, n):
    buff = b''
    while n > 0:
        b = sock.recv(n)
        buff += b
        if len(b) == 0:
            raise EOFError          # peer socket has received a SH_WR shutdown
        n -= len(b)
    return buff

可能效率不高,因为如果我们要求大量的字节,并且数据如果非常碎片化,我们会反复重新分配一个新的字节缓冲区。

问题:

怎样才能可靠地从流套接字准确接收 n 个字节而没有重新分配的风险?

参考:

这些其他问题是相关的,并且确实给出了提示,但没有一个给出简单而明确的答案:

标签: pythonsockets

解决方案


解决方案是使用recv_into和一个memoryview. Python 允许预先分配一个可修改bytearray的可传递给recv_into. 但是您不能将数据接收到字节数组的切片中,因为切片将是一个副本。但是 amemoryview允许将多个片段接收到同一个中bytearray

def readnbyte(sock, n):
    buff = bytearray(n)
    pos = 0
    while pos < n:
        cr = sock.recv_into(memoryview(buff)[pos:])
        if cr == 0:
            raise EOFError
        pos += cr
    return buff

推荐阅读