2010-01-10 10 views
17

Próbuję napisać aplikację, która wykorzystuje bufory protokołów Google do deserializacji danych (wysyłanych z innej aplikacji przy użyciu buforów protokołu) przez połączenie TCP. Problem polega na tym, że wygląda na to, że bufory protokołów w Pythonie mogą deserializować dane tylko z łańcucha. Ponieważ TCP nie ma dobrze zdefiniowanych granic komunikatów i jedna z wiadomości, które próbuję odebrać, ma powtarzające się pole, nie będę wiedział, ile danych próbować i odbierać przed ostatecznym przekazaniem ciągu do deserializacji.Jak używać Pythona i buforów protokołów Google do deserializacji danych przesyłanych przez TCP

Czy są jakieś dobre praktyki do robienia tego w Pythonie?

Odpowiedz

36

Nie pisz tylko zserializowanych danych do gniazda. Najpierw wyślij pole o stałym rozmiarze zawierające długość serializowanego obiektu.

strona wysyłająca jest grubsza:

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

i stroną recv'ing staje się coś takiego:

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

Jest to wspólny wzorzec projektowy do programowania sieciowego. Większość projektów rozszerzyć strukturę over-the-wire zawierać pole typu, tak więc twoja strona przyjmująca staje się czymś w rodzaju:

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) 

skończyć z formatu over-the-wire wiadomości, która wygląda tak:

struct { 
    unsigned char type; 
    unsigned short length; 
    void *data; 
} 

Wykonuje rozsądną pracę polegającą na zabezpieczeniu protokołu transmisji przed nieprzewidzianymi wymaganiami. Jest to protokół Type-Length-Value, który można znaleźć wielokrotnie w protokołach sieciowych.

+1

+1 za niezwykle szczegółową i wspaniałą odpowiedź. Dziękuję Ci!! – jathanism

+2

Użycie 'struct.pack (" H ", len (dane)) prowadzi do ważnej konsekwencji: dane muszą mieć mniej niż 65536 bajtów. Można zwiększyć maksymalny dozwolony rozmiar danych, używając bez znaku długiego long zamiast "Q" (maksymalny rozmiar = 18000 petabajtów). – Flimm

4

, aby rozwinąć odpowiedź J.J. (całkowicie poprawną), biblioteka protobuf ma nie sposób, aby dowiedzieć się, jak długo wiadomości są na własną rękę, lub dowiedzieć się, jaki typ obiektu protobuf jest wysyłany *. Tak więc druga aplikacja wysyłająca dane musi już coś takiego robić.

Gdy miałem to zrobić, I wdrożone tabeli odnośników:

messageLookup={0:foobar_pb2.MessageFoo,1:foobar_pb2.MessageBar,2:foobar_pb2.MessageBaz} 

... i tak w zasadzie to, co J. J. zrobił, ale miałem również funkcję pomocnika:

def parseMessage(self,msgType,stringMessage): 
     msgClass=messageLookup[msgType] 
     message=msgClass() 
     message.ParseFromString(stringMessage) 
     return message 

... który zadzwoniłem, aby zmienić ciąg w obiekt protobuf.

(*) Myślę, że to możliwe, aby uzyskać okrągłe to przez umieszczenie określonych komunikatów wewnątrz wiadomości pojemnika

+0

Obie odpowiedzi są dobre, ale frymastry nie na enkapsulacji to (według mnie) droga naprzód. –

0

inny aspekt pod uwagę (choć dla prostszej przypadku) to gdzie można korzystać z jednego połączenia TCP dla pojedynczej wiadomości . W takim przypadku, dopóki wiesz, jaki jest oczekiwany komunikat (lub użyj Union Types do określenia typu komunikatu w czasie wykonywania), możesz użyć połączenia TCP otwartego jako ogranicznika "start" i zdarzenia zamknięcia połączenia jako ostateczny ogranicznik. Ma to tę zaletę, że szybko otrzymasz całą wiadomość (podczas gdy w innych przypadkach strumień TCP może być przechowywany przez pewien czas, opóźniając otrzymanie całej wiadomości). Jeśli to zrobisz, nie potrzebujesz żadnego jawnego kadrowania w paśmie, ponieważ czas życia połączenia TCP działa jak sama ramka.

Powiązane problemy