我正在尝试使用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数据,仅此而已。非常感谢所有给出答复的人。希望这对外面的人有帮助。
答案 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没有为您提供帮助执行此循环的助手–您可以在中跟踪其进度。同时,可以自己编写。我认为类似这样的方法应该起作用:
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