protobuf.net Десериализатор ожидает неопределенное время

#c# #protocol-buffers #protobuf-net

#c# #протокол-буферы #protobuf-net

Вопрос:

У меня есть простой TcpListener, который прослушивает соединения, и как только соединение установлено, метод пытается отправить данные, а серверная сторона пытается их прочитать.

на стороне клиента:

 using (NetworkStream stream = new NetworkStream(_client.Client, false))
            {
                Serializer.Serialize(stream, MyPersonObject);
            }
 

На стороне сервера:

 using (NetworkStream stream = new NetworkStream(_client.Client, false))
            {
                var myObject = Serializer.DeSerialize<Person>(stream);
            }
 

Однако я заметил, что как только он попадает в метод десериализации, он зависает и ждет бесконечно.
Обратите внимание, что этого НЕ происходит с BinaryFormatter, использующим те же самые шаги. я не уверен, что не так.

Ответ №1:

Поток protobuf не является «закрытым» — по умолчанию он считывается до конца потока, что означает, что он будет считываться до тех пор, пока входящий TCP-сокет не будет помечен как завершенный.

Если вы собираетесь отправлять несколько сообщений, попробуйте использовать версии «WithLengthPrefix» для сериализации и десериализации; это добавляет вам обрамление сообщений, позволяя ему знать, где заканчивается каждая полезная нагрузка.

Комментарии:

1. Привет, Марк. Вы хотите сказать, что он ждет бесконечно, потому что сокет TCP не помечен как завершенный? Что вы подразумеваете под «помечено как завершенное»? Если он читается до конца steam, разве он не должен выходить после этого? Я пробовал это с Memory-stream, и, похоже, все работает нормально.

2. @Enlight в этом контексте я имею в виду, что входящий сокет приема (отправляющий сокет источника) должен был быть закрыт. В противном случае поток открыт. Чтобы отправить несколько сообщений в сокет, вам нужно создать фрейминг; это то, что обеспечивает «WithLengthPrefix». В отличие от BinaryFormatter было встроено обрамление, и это нормально: но формат protobuf определяется Google, а не мной, поэтому я не могу добавлять его в одностороннем порядке 🙂 если вы отправляете только одно сообщение: просто закройте канал отправки после записи.

3. @Enlight причина этого в том, что фактический формат кодирования допускает сообщения длиннее или короче, чем фактический объект, который вы ожидали (например, для «неизвестных полей» из более поздней версии протокола или объединенных полей). Если у вас нет маркера длины, декодер не знает, ожидаете ли вы еще больше данных в потоке, пока он все еще открыт.

4. И вы сказали, что он отлично работает при использовании MemoryStream; это потому, что поток памяти (и файла) имеет известную длину (т. Е. Определенный «конец» потока). Сетевой поток не имеет определенной длины ; и если вы не закроете его, система не сможет узнать, что обработка данных завершена.

5. По длине вы также имеете в виду фрейминг сообщений? отправляется байт, чтобы сообщить получателю, сколько байтов в следующем сообщении? И поскольку в NetworkStream нет фрейминга сообщений, вы можете отправить только одну вещь, а затем закрыть сокет, чтобы получатель получил