18

Google のプロトコル バッファを使用して、TCP 接続を介して (プロトコル バッファを使用して別のアプリケーションから送信された) データを逆シリアル化するアプリケーションを作成しようとしています。問題は、Python のプロトコル バッファが文字列からのデータしか逆シリアル化できないように見えることです。TCP には明確に定義されたメッセージ境界がなく、受信しようとしているメッセージの 1 つに繰り返しフィールドがあるため、最終的に逆シリアル化する文字列を渡す前に、どれだけのデータを受信しようとするかわかりません。

Pythonでこれを行うための良い方法はありますか?

4

3 に答える 3

36

シリアル化されたデータをソケットに書き込むだけではいけません。最初に、シリアライズされたオブジェクトの長さを含む固定サイズのフィールドを送信します。

送信側は大まかに次のとおりです。

socket.write(struct.pack("H", len(data))    #send a two-byte size field
socket.write(data)

そして、受信側は次のようになります。

dataToRead = struct.unpack("H", socket.read(2))[0]    
data = socket.read(dataToRead)

これは、ソケット プログラミングの一般的な設計パターンです。ほとんどの設計では、有線構造を拡張して type フィールドも含めるため、受信側は次のようになります。

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;
}

これは、予想外の要件に対するワイヤ プロトコルの将来性を保証する合理的な仕事をします。これはType-Length-Valueプロトコルであり、ネットワーク プロトコルで何度も何度も使用されます。

于 2010-01-10T19:06:16.390 に答える
4

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オブジェクトに変換するために呼び出しました。

(*) コンテナー メッセージ内に特定のメッセージをカプセル化することで、これを回避できると思います

于 2010-01-11T15:26:30.913 に答える
0

(より単純なケースではありますが) 考慮すべきもう 1 つの側面は、1 つのメッセージに対して 1 つの TCP 接続を使用する場合です。この場合、予想されるメッセージが何であるかを知っている限り (または実行時にユニオン タイプを使用してメッセージ タイプを決定する)、TCP 接続オープンを「開始」デリミタとして使用し、接続クローズ イベントを次のように使用できます。最後の区切り文字。これには、メッセージ全体をすばやく受信できるという利点があります (一方、TCP ストリームがしばらく保持され、メッセージ全体の受信が遅れる場合もあります)。これを行う場合、TCP 接続の有効期間がフレーム自体として機能するため、明示的なインバンド フレーミングは必要ありません。

于 2013-10-27T21:22:13.337 に答える