Używamy protobuf-net do serializacji i deserializacji wiadomości w aplikacji, której protokół publiczny oparty jest na buforach protokołu Google. Biblioteka jest znakomita i obejmuje wszystkie nasze wymagania, z wyjątkiem tego: musimy dowiedzieć się, jak serializowana jest długość wiadomości w bajtach, zanim wiadomość zostanie serializowana.protobuf-net serializowany rozmiar właściwości
The question już zapytał półtora roku temu i według Marc, jedynym sposobem, aby to zrobić, to do serializacji do MemoryStream i odczytać właściwość .Length
później. Jest to niedopuszczalne w naszym przypadku, ponieważ MemoryStream przydziela bufor bajtów za kulisami i musimy tego uniknąć.
Ta linia z tej samej odpowiedzi daje nam nadzieję, że to może być możliwe po wszystkim:
Jeśli wyjaśnienie co use-case jest, jestem pewien, że możemy zrobić to łatwo dostępne (jeśli to jeszcze nie jest).
Oto nasz przypadek użycia. Mamy wiadomości, których rozmiar waha się od kilku bajtów do dwóch megabajtów. Aplikacja wstępnie alokuje bufory bajtów wykorzystywane w operacjach na gniazdach oraz do serializowania/deserializacji, a po fazie rozgrzewania nie można utworzyć żadnych dodatkowych buforów (wskazówka: kodowanie GC i fragmentacja sterty). Bufory bajtów są zasadniczo łączone. Chcemy także unikać kopiowania bajtów między buforami/strumieniami w jak największym stopniu.
Mamy pochodzić z dwóch możliwych strategii i obie z nich wymagają rozmiar wiadomości upfront:
- Użyj (duży) o stałej wielkości buforów bajtowych i szeregować wszystkie wiadomości, które można dopasować do jednego bufora; wyślij zawartość bufora przy użyciu
Socket.Send
. Musimy wiedzieć, kiedy następna wiadomość nie może się zmieścić w buforze i przestać serializować. Bez rozmiaru wiadomości jedynym sposobem, aby to osiągnąć, jest oczekiwanie na wystąpienie wyjątku podczasSerialize
. - Użyj (małych) buforów bajtów o zmiennym rozmiarze i serializuj każdą wiadomość w jeden bufor; wyślij zawartość bufora przy użyciu
Socket.Send
. Aby sprawdzić bufor bajtowy o odpowiednim rozmiarze z puli, musimy wiedzieć, ile bajtów ma zserializowana wiadomość.
Ponieważ protokół jest już zdefiniowany (nie możemy tego zmienić) i wymaga prefiksu długości wiadomości jako Varint32, nie możemy użyć metody SerializeWithLengthPrefix
.
Czy jest więc możliwe dodanie metody do oszacowania rozmiaru wiadomości bez serializacji w strumieniu? Jeśli jest to coś, co nie pasuje do obecnego zestawu funkcji i mapy drogowej biblioteki, ale jest wykonalne, jesteśmy zainteresowani samodzielnym rozszerzeniem biblioteki. Szukamy także alternatywnych podejść, jeśli takie istnieją.
Odnośnie przedrostka długości i SerializeWithLengthPrefix: metoda może zapisywać prefiksy zakodowane jako Base128, Fixed32 i Fixed32BigEndian, ale nie obsługuje typu Varint32. Nasz protokół definiuje następującą strukturę wiadomości: [type: varint32] [length: varint32] [aktualny komunikat protobuf]. –
@Boris, który jest dokładnie tym, co baza-128 z numerem pola ** jest **. Numer pola będzie === typ (prawdopodobnie z przesunięciem >> 3). Musiałbym zobaczyć * dokładne bajty *, ale jest to prawdopodobnie użyteczne. Jeśli nie, po prostu dodaj typ ręcznie i zakoduj przy pomocy base-128 i pola 0 (które będzie pomijało numer pola). –
Dzięki za wyjaśnienie, myślę, że tęskniłem za tym. I dziękuję za szczegółową odpowiedź. Wciąż analizujemy możliwe podejścia i staramy się znaleźć optymalny sposób przy możliwie jak najmniejszym żonglowaniu danymi i przydzielaniu buforów. –