Исключение двоичного форматирования

#c# #.net #exception #serialization #binaryformatter

#c# #.net #исключение #сериализация #двоичный форматировщик

Вопрос:

Я пытаюсь переместить объектный граф из серверного процесса в клиентский. И это работает. По крайней мере, это работает, когда и клиент, и сервер находятся на моей виртуальной машине разработчика. Это также работает, когда я запускаю сервер на своей базовой машине (клиент на виртуальной машине разработчика).

Однако он перестает работать, когда я запускаю сервер на своем КОМПЬЮТЕРЕ media center. Исключением является:

Двоичный поток ‘0’ не содержит допустимого двоичного заголовка. Возможные причины — недопустимое изменение версии потока или объекта между сериализацией и десериализацией.

Все три компьютера являются 64-разрядными компьютерами с Windows 7. Я использую TcpClient и TcpListener вместе с BinaryFormatter классом для выполнения тяжелой работы.

Передаваемые данные считываются из файла с использованием стандартного FileStream объекта.

Если на стороне клиента я сериализую буферы в файл, содержимое (согласно BeyondCompare), похоже, действительно отличается ?!?

Все строковые свойства моих объектов закодированы в Base64 в установщиках и декодированы в получателях.

Я могу опубликовать код, но я не уверен, где находится проблемная область? Есть идеи?

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

1. Убедитесь, что все загруженные сборки на самом деле являются одинаковыми версиями и представляют собой одну и ту же сборку. Кроме того, я не понимаю смысла использования кодировки Base64 в сценарии двоичной сериализации.

2. @leppie, да — все сборки определенно идентичны. Элемент base64 был только что добавлен в последней попытке изолировать любую странность кодировки — я определенно не эксперт в использовании BinaryFormatter.

Ответ №1:

Обновление: похоже, я решил эту проблему. У меня была точка останова, когда клиент прочитал ответ сервера

 tcpClient.GetStream().Read(buffer, 0, buffer.Length);
  

и отметил, что с «проблемного» сервера было прочитано меньше байтов. После быстрого поиска в Google я нашел эту статью http://social.msdn.microsoft.com/Forums/en-US/ncl/thread/759f3f2f-347b-4bd8-aa05-fb7f681c3426 в котором Дейв Мюррей предлагает:

Есть пара способов справиться с этим более элегантно. Если вы не планируете повторно использовать соединение для чего-либо еще, проще всего то, что предлагает nobugz. Когда сервер закончит отправку данных, закройте его (), это конец соединения. Когда клиент прочитает все данные, отправленные перед закрытием, Read начнет возвращать 0, и в этот момент вы узнаете, что сервер не планирует отправлять что-либо еще.

Итак, я обновил свой код с одного чтения до:

 var buffer = new byte[32768];
var totalBytesRead = 0;
var bytesRead = tcpClient.GetStream().Read(buffer, 0, buffer.Length);

do
{
      totalBytesRead  = bytesRead;
      bytesRead = tcpClient.GetStream().Read(buffer, totalBytesRead, bytesRead);

} while (bytesRead > 0);
  

и обновил мой серверный код, чтобы закрыть соединение в соответствии с сообщением. И, согласно комментарию от @leppie, я, вероятно, смогу удалить свои свойства, обернутые в Base64…

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

1. Да, не проверять результат Read является ошибкой. Фиксированный буфер также является ошибкой, если только вы не знаете длину заранее. Предпочтительным подходом было бы поместить его в MemoryStream . Или: просто десериализуйте непосредственно из потока TCP.

2. @Marc — Спасибо, согласен — это POC-код, но это не оправдывает плохое кодирование. На данный момент я удалю буфер фиксированного размера и буду читать непосредственно в поток памяти. Повторите «десериализацию прямо из потока TCP». Мой вопрос заключался бы в том, что если TcpClient читает фрагментами, как это будет работать? Вам нужно прочитать все сообщение до десериализации?

3. поскольку у вас есть TCP-клиент в качестве Stream , вам не нужно все сообщение; вызывающий может извлекать данные по своему усмотрению