如何在Google协议缓冲区中使用python-trio?

时间:2019-06-14 05:59:57

标签: python protocol-buffers python-trio

我正在尝试使用python中的protobuf读取一些数据流,并且我想使用trio来使客户端读取数据流。 protobuf有一些方法调用,当我使用三重奏流时,我发现它们不起作用。

Linux机器上的Python客户端。

import DTCProtocol_pb2 as Dtc

async def parent(addr, encoding, heartbeat_interval):
    print(f"parent: connecting to 127.0.0.1:{addr[1]}")
    client_stream = await trio.open_tcp_stream(addr[0], addr[1])

    # encoding request
    print("parent: spawing encoding request ...")
    enc_req = create_enc_req(encoding) # construct encoding request
    await send_message(enc_req, Dtc.ENCODING_REQUEST,client_stream, 'encoding request') # send encoding request

    log.debug('get_reponse: started')
    response = await client_stream.receive_some(1024)
    m_size = struct.unpack_from('<H', response[:2]) # the size of message
    m_type = struct.unpack_from('<H', response[2:4]) # the type of the message
    m_body = response[4:]
    m_resp = Dtc.EncodingResponse()

m_body是一些字节数据,我不知道该如何解码。 Dtc.EncodingResponse()是protobuf方法,它将提供一个Dtc对象,该对象包含可读格式的响应。 (Dtc是protobuf文件)。但是我什么都没得到。当我在没有三重奏的情况下执行此脚本时,Dtc.EncodingResponse()将以可读格式给出完整的响应。

我猜测问题是“ client_stream”是仅读取字节的三重流对象,因此我可能需要使用ReceiveChannel对象。但是,如果这是真的,我不知道该怎么做。

更新: 纳撒尼尔·史密斯(Nathaniel J. Smith)的以下答案解决了我的问题。

m_resp = Dtc.EncodingResponse()
m_resp.ParseFromString(m_body)

我觉得很傻,但是我之前没有解析ParseFromString数据,仅此而已。非常感谢所有给出答复的人。希望这对外面的人有帮助。

1 个答案:

答案 0 :(得分:1)

就像@shmee在评论中说的那样,我认为您的代码因编辑而有些混乱...您应该仔细检查。

  

当我在没有三重奏的情况下执行此脚本时,Dtc.EncodingResponse()将以可读格式给出完整的响应

我认为您切换到Trio时可能会掉线吗? Dtc.EncodingResponse()仅创建一个新的空EncodingResponse对象。如果您想将m_body中的数据解析到新对象中,则必须使用类似以下内容的方法来明确地做到这一点:

m_resp = Dtc.EncodingResponse()
m_resp.ParseFromString(m_body)

但是,还有另一个问题...之所以称为receive_some是因为它接收到 some 个字节,但可能没有收到您请求的 all 个字节对于。您的代码假设对receive_some的单次调用将获取响应中的所有字节,当您执行简单测试时可能是正确的,但通常不能保证。如果在第一次调用receive_some时没有得到足够的数据,则可能需要不断重复调用它,直到获得所有数据为止。

这实际上是非常标准的...套接字的工作方式相同。这就是为什么服务器在开始时首先发送m_size字段的原因-这样您就可以知道是否已获取所有数据!

不幸的是,截至2019年6月,Trio没有为您提供帮助执行此循环的助手–您可以在enter image description here中跟踪其进度。同时,可以自己编写。我认为类似这样的方法应该起作用:

async def receive_exactly(stream, count):
    buf = bytearray()
    while len(buf) < count:
        new_data = await stream.receive_some(count - len(buf))
        if not new_data:
            raise RuntimeError("other side closed the connection unexpectedly")
        buf += new data
    return buf

async def receive_encoding_response(stream):
    header = await receive_exactly(stream, 4)
    (m_size, m_type) = struct.unpack('<HH', header)
    m_body = await receive_exactly(stream, m_size)
    m_resp = Dtc.EncodingResponse()
    m_resp.ParseFromString(m_size)
    return m_resp