[英]How to use Python and Google's Protocol Buffers to deserialize data sent over TCP
我正在嘗試編寫一個應用程序,該應用程序使用Google的協議緩沖區通過TCP連接反序列化數據(使用協議緩沖區從另一個應用程序發送)。 問題是,看起來Python中的協議緩沖區只能從字符串反序列化數據。 由於TCP沒有明確定義的消息邊界,並且我嘗試接收的消息之一具有重復字段,因此在最終傳遞要反序列化的字符串之前,我不知道要嘗試和接收多少數據。
在Python中執行此操作有什么好的做法嗎?
不要只是將序列化數據寫入套接字。 首先發送一個包含序列化對象長度的固定大小的字段。
發送方大致是:
socket.write(struct.pack("H", len(data)) #send a two-byte size field
socket.write(data)
recv'ing方面變得像:
dataToRead = struct.unpack("H", socket.read(2))[0]
data = socket.read(dataToRead)
這是套接字編程的常見設計模式。 大多數設計擴展了線上結構以包括類型字段,因此您的接收方將變為:
type = socket.read(1) # get the type of msg
dataToRead = struct.unpack("H", socket.read(2))[0] # get the len of the msg
data = socket.read(dataToRead) # read the msg
if TYPE_FOO == type:
handleFoo(data)
elif TYPE_BAR == type:
handleBar(data)
else:
raise UnknownTypeException(type)
您最終得到了一種線上消息格式,如下所示:
struct {
unsigned char type;
unsigned short length;
void *data;
}
這樣可以有效地解決線路協議面臨無法預料的需求。 它是一種類型 - 長度 - 值協議,您可以在網絡協議中一次又一次地找到它。
為了擴展JJ的(完全正確的)答案,protobuf庫沒有辦法計算出消息本身有多長,或者計算出正在發送什么類型的protobuf對象*。 因此,向您發送數據的其他應用程序必須已經在執行此類操作。
當我不得不這樣做時,我實現了一個查找表:
messageLookup={0:foobar_pb2.MessageFoo,1:foobar_pb2.MessageBar,2:foobar_pb2.MessageBaz}
...並且基本上做了JJ所做的事,但我也有一個輔助函數:
def parseMessage(self,msgType,stringMessage):
msgClass=messageLookup[msgType]
message=msgClass()
message.ParseFromString(stringMessage)
return message
...我打電話將字符串轉換為protobuf對象。
(*)我認為可以通過在容器消息中封裝特定消息來解決這個問題
要考慮的另一個方面(盡管是更簡單的情況)是您為單個消息使用單個TCP連接。 在這種情況下,只要您知道預期的消息是什么(或使用聯合類型在運行時確定消息類型),您可以使用TCP連接打開作為“開始”分隔符,並將連接關閉事件用作最后的分隔符。 這樣做的好處是,您可以快速接收整個消息(而在其他情況下,TCP流可以保留一段時間,從而延遲收到整個消息)。 如果這樣做,則不需要任何明確的帶內成幀,因為TCP連接的生命周期充當幀本身。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.