Я пытаюсь записать приложение, которое использует буферы протокола Google для десериализации данных (отправленный от другого приложения с помощью буферов протокола) по соединению TCP. Проблема состоит в том, что выглядит, как будто буферы протокола в Python могут только десериализовать данные из строки. Так как TCP не имеет четко определенных границ сообщения и одного из сообщений, которые я пытаюсь получить, имеет повторное поле, я не буду знать сколько данных пытаться получить прежде наконец передать строку, которая будет десериализована.
Есть ли какие-либо хорошие методы для того, чтобы сделать это в Python?
Не просто записывайте сериализованные данные в сокет. Сначала отправьте поле фиксированного размера, содержащее длину сериализованного объекта.
Отправляющая сторона приблизительно:
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 = 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 , который вы снова и снова найдете в сетевых протоколах.
, чтобы расширить ответ J.J. (совершенно верно), у библиотеки protobuf нет возможности выяснить, как долго сообщения находятся сами по себе, или выяснить, какой тип объекта protobuf посылается*. Так что другое приложение, которое посылает вам данные, должно уже делать что-то подобное.
Когда мне пришлось это сделать, я реализовал lookup table:
messageLookup={0:foobar_pb2.MessageFoo,1:foobar_pb2.MessageBar,2:foobar_pb2.MessageBaz}
...и сделал по существу то же, что и J.J., но у меня также была вспомогательная функция:
def parseMessage(self,msgType,stringMessage):
msgClass=messageLookup[msgType]
message=msgClass()
message.ParseFromString(stringMessage)
return message
...которую я вызвал, чтобы превратить строку в протобуф-объект.
(*) Я думаю, что можно обойти это, инкапсулируя специфические сообщения внутри сообщения контейнера
.