Двоичный поток "226" не содержит допустимый BinaryHeader - C#
У меня есть типичный "Двоичный поток" x, который не содержит действительного BinaryHeader ", но все вопросы, которые я просматривал в Интернете, похоже, не решают мою конкретную ситуацию.
Детали вопроса
Часть программы, о которой идет речь, в своих основных терминах записывает данные класса в двоичный файл. Это работает безупречно в 99,9% случаев, но недавно мы обнаружили неясный способ испортить данные.
Таким образом, класс, который сериализуется, является открытым классом с именем 'RecordEntry', помеченным [Serializable]
приписывать. Класс наследуется от интерфейса. RecordEntry содержит множество переменных с информацией, но та, которая вызывает у нас проблему, - это массив класса с именем EntryField. Это очень простой класс, который содержит 3 строки и также помечен как [Serializable]
,
Если массив классов EntryField (назовем его EntryFieldArray), имеет длину 4, а первая строка в каждом EntryField имеет длину 13, а третья строка в каждом имеет длину 1, затем при попытке десериализации я получаю ошибку в заголовке. Вероятно, есть еще несколько неясных способов воспроизвести тот же сбой, но это тот, который я нашел прямо сейчас.
Код сериализации
Во-первых, класс RecordEntry преобразуется в байты:
public static byte[] ToBytes(this object obj)
{
using (var stream = new MemoryStream())
{
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, obj);
return stream.ToArray();
}
}
Этот байтовый массив затем добавляется в файл как блок данных.
Для считывания зашифрованного файла сначала считываются байты обратно в определенные блоки, а затем дешифруются с использованием следующего:
internal static T ObjectFromBytes<T>(this byte[] bytes)
{
using (var stream = new MemoryStream(bytes))
{
IFormatter formatter = new BinaryFormatter();
return (T)formatter.Deserialize(stream);
}
}
Вот важная часть трассировки стека для отслеживания ошибки:
System.Runtime.Serialization.SerializationException : Binary stream '226' does not contain a valid BinaryHeader. Possible causes are invalid stream or object version change between serialization and deserialization.
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
at <ProjectName>.<ProjectFile>.ObjectFromBytes[T](Byte[] bytes)
Кажется, я не могу отследить эту проблему, и некоторые разработчики, с которыми я связался, сказали, что это может быть проблема с кодом сериализации Microsoft. Любые идеи о том, что может вызвать такую проблему?
1 ответ
Вы пишете о шифровании... Возможно, есть какая-то проблема с тем, как выполняется шифрование... Лучшее, что вы можете сделать, это добавить некоторые проверки в ваши сериализованные данные.
// https://en.wikipedia.org/wiki/Jenkins_hash_function
public static uint JenkinsOneAtATimeHash(byte[] key, int start, int count)
{
int i = start;
int end = start + count;
uint hash = 0;
while (i != end)
{
hash += key[i++];
hash += hash << 10;
hash ^= hash >> 6;
}
hash += hash << 3;
hash ^= hash >> 11;
hash += hash << 15;
return hash;
}
public static byte[] ToBytes(this object obj)
{
using (var stream = new MemoryStream())
{
IFormatter formatter = new BinaryFormatter();
// We will prepend the length of the serialized object, we leave some space
stream.Position += sizeof(int);
formatter.Serialize(stream, obj);
// We append a Jenkins hash of the serialized object
uint hash = JenkinsOneAtATimeHash(stream.GetBuffer(), 4, (int)stream.Length - sizeof(int));
byte[] buffer = BitConverter.GetBytes(hash);
stream.Write(buffer, 0, buffer.Length);
// We prepend the length of the serialized object (max 2gb)
buffer = BitConverter.GetBytes((int)stream.Length - sizeof(int) - sizeof(int));
stream.Position = 0;
stream.Write(buffer, 0, buffer.Length);
return stream.ToArray();
}
}
internal static T ObjectFromBytes<T>(this byte[] bytes)
{
if (bytes.Length < sizeof(int) + sizeof(int))
{
throw new Exception(string.Format("Serialized length: {0} < {1}", bytes.Length, sizeof(int) + sizeof(int)));
}
int length = BitConverter.ToInt32(bytes, 0);
if (length != bytes.Length - sizeof(int) - sizeof(int))
{
throw new Exception(string.Format("Serialized length should be {0}, is {1} (+ sizeof(int) * 2)", length, bytes.Length - sizeof(int) - sizeof(int)));
}
uint hash = BitConverter.ToUInt32(bytes, bytes.Length - 4);
uint hash2 = JenkinsOneAtATimeHash(bytes, sizeof(int), bytes.Length - sizeof(int) - sizeof(int));
if (hash != hash2)
{
throw new Exception("Wrong hash!");
}
using (var stream = new MemoryStream(bytes, sizeof(int), bytes.Length - sizeof(int) - sizeof(int)))
{
IFormatter formatter = new BinaryFormatter();
return (T)formatter.Deserialize(stream);
}
}
Я изменил методы сериализации и десериализации. Теперь я добавляю длину объекта и добавляю простой хеш. Это не очень сильный хеш, но этого должно быть достаточно для обнаружения изменений в данных. После десериализации я делаю некоторые проверки длины потока и хэша.