【问题标题】:How to use python-trio with google protocol buffer?如何将 python-trio 与谷歌协议缓冲区一起使用?
【发布时间】:2019-06-17 17:44:18
【问题描述】:

我正在尝试在 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 数据,仅此而已。非常感谢所有回复的人。希望这对那里的人有所帮助。

【问题讨论】:

  • 我编辑了这个问题,我要告诉你一些事情:永远不要为成为“菜鸟”而道歉。学习很好!
  • 谢谢!真的很感谢鼓励!
  • 虽然我完全同意 @M.K 关于为自己是新手而道歉的观点,但我认为删除从 # encoding request 开始的代码行的缩进会破坏代码。名称 client_stream 是在 parent 协程的本地范围内创建的,但现在可以在其外部访问。此外,以parent: 开头的print 在某种程度上暗示这仍然应该是coro 主体的一部分。你能验证一下吗?
  • @shmee ,你是对的。我的代码格式错误。我已经编辑了正确的空格。感谢您热情温和的建议。

标签: python protocol-buffers python-trio


【解决方案1】:

就像@shmee 在评论中所说的那样,我认为您的代码被编辑破坏了一些......您应该仔细检查。

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

我认为您在切换到 Trio 时可能会掉线? Dtc.EncodingResponse() 只是创建一个新的空 EncodingResponse 对象。如果您想将 m_body 中的数据解析到您的新对象中,您必须明确地执行此操作,例如:

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

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

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

很遗憾,截至 2019 年 6 月,Trio 没有提供帮助来为您执行此循环 - 您可以在 this issue 中跟踪其进度。同时,也可以自己编写。我认为这样的事情应该可行:

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

【讨论】:

  • 非常感谢您的回复,将 m_body 中的数据解析为新对象确实为我提供了可读格式的数据。我觉得很傻,解决办法就是这么简单。但我仍然非常感激。
猜你喜欢
  • 2010-11-06
  • 2012-11-25
  • 1970-01-01
  • 2011-09-18
  • 2016-12-19
  • 1970-01-01
  • 2012-12-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多