"Недопустимое поле в исходных данных: ошибка 0" с ProtoBuf-Net и Compact Framework

Кто-нибудь знает о каких-либо проблемах при использовании ProtoBuf-Net для сериализации/десериализации между компактной картой и полной картой .Net? У меня есть класс LogData, который я сериализую в компактной структуре 3.5, передавая на сервер (работающий .Net framework 4.0), который затем десериализуется. Иногда он срабатывает, и иногда он выдает указанную выше ошибку, и мне еще предстоит сузить ее до какой-либо конкретной причины. Я сделал много тестов с разными значениями и не могу найти никаких рифм или причин, когда возникает ошибка. Я включаю в себя мои классы ниже (минус различные конструкторы). Я несколько раз смотрел на байтовый буфер с обеих сторон и еще не нашел разницы в данных, отправляемых по проводу с одной стороны на другую.

[ProtoContract]
public class LogData
{

  [ProtoContract]
  public enum LogSeverity
  {
     [ProtoEnum(Name = "Information", Value = 0)]
     Information,
     [ProtoEnum(Name = "Warning", Value = 1)]
     Warning,
     [ProtoEnum(Name = "Error", Value = 2)]
     Error,
     [ProtoEnum(Name = "Critical", Value = 3)]
     Critical
  }

  [ProtoMember(1)]
  public string UserID { get; set; }
  [ProtoMember(2)]
  public string ComputerName { get; set; }
  [ProtoMember(3)]
  public ExceptionProxy Exception { get; set; }
  [ProtoMember(4)]
  public LogData.LogSeverity Severity { get; set; }
  [ProtoMember(5)]
  public string Source { get; set; }
  [ProtoMember(6)]
  public string Caption { get; set; }
  [ProtoMember(7)]
  public string Description { get; set; }
  [ProtoMember(8)]
  public DateTime TimeOfOccurrence { get; set; }
  [ProtoMember(9)]
  public Guid SessionID { get; set; }
  [ProtoMember(10)]
  public string MethodName { get; set; }
  [ProtoMember(11)]
  public string OSVersion { get; set; }
  [ProtoMember(12)]
  public string Category { get; set; }
  [ProtoMember(13)]
  public string Location { get; set; }
}

[ProtoContract]
public class ExceptionProxy
{

  [ProtoMember(1)]
  public Type ExceptionType { get; set; }
  [ProtoMember(2)]
  public string Message { get; set; }
  [ProtoMember(3)]
  public string StackTrace { get; set; }
  [ProtoMember(4)]
  public ExceptionProxy InnerException { get; set; }

}

Вот мой код, который выполняет сериализацию и отправку

  private void WriteLogDataToServer(LogData data)
  {
     using (var client = new TcpClient())
     {
        client.Connect(Host, SignalLineServerPort);
        using (var stream = client.GetStream())
        {
           using (var ms = new MemoryStream())
           {
              Serializer.Serialize<LogData>(ms, data);
              var buffer = ms.GetBuffer();
              int position = 0;
              WriteFrameMarkers(stream);
              byte[] frameLengthBuffer = BitConverter.GetBytes(buffer.Length);
              stream.Write(frameLengthBuffer, 0, IntByteSize);
              while (position < buffer.Length)
              {
                 int length = Math.Min(ChunkSize, buffer.Length - position);
                 stream.Write(buffer, position, length);
                 position += ChunkSize;
              }
           }
        }
        client.Close();
     }         
  }

И это код, который считывает данные на сервере

  public override LogData ReadData(NetworkStream stream)
  {
     if (stream.DataAvailable)
     {
        try
        {
           const int chunkSize = 250;
           byte[] buffer = new byte[IntByteSize];
           int messageSize = 0;
           int totalBytesRead = 0;
           LogData data;
           using (var ms = new MemoryStream())
           {
              if (!ReadFrameMarkers(stream))
                 return null;
              totalBytesRead = stream.Read(buffer, 0, IntByteSize);
              if (totalBytesRead != IntByteSize)
                 return null;
              messageSize = BitConverter.ToInt32(buffer, 0);
              totalBytesRead = 0;
              while ((totalBytesRead < messageSize))
              {
                 int bufferSize = Math.Min(chunkSize, messageSize - totalBytesRead);
                 buffer = new byte[bufferSize];
                 int bytesRead = stream.Read(buffer, 0, bufferSize);
                 if (bytesRead != 0)
                 {
                    totalBytesRead += bytesRead;
                    ms.Write(buffer, 0, bytesRead);
                 }
              }
              ms.Seek(0, SeekOrigin.Begin);
              data = Serializer.Deserialize<LogData>(ms);
           }
           return data;
        }
        catch (Exception ex)
        {
           Console.WriteLine(string.Format("Error occurred: {0}", ex.Message));
           return null;
        }
     }
     return null;
  }

Ответ 1

Легкий: вы используете:

var buffer = ms.GetBuffer();

И затем buffer.Length. Это означает, что вы используете негабаритный буферизированный буфер. Если вы это сделаете, вам нужно использовать ms.Length, который скажет вам фактическую длину. Альтернативно, может использоваться ms.ToArray(), но это включает дополнительную копию.

Мой совет: продолжайте использовать GetBuffer(), но только пишите ms.Length байты, а не buffer.Length байты.

Как только вы удалите эти лишние неправильные нули, я надеюсь, вы обнаружите, что он работает.

Ответ 2

Я понимаю, что главный разработчик @MarcGravell уже ответил, но я просто хотел поделиться своими собственными $0.02, которые помогли мне в этом вопросе. Если у меня есть фиксированный байт размера [] и получить количество байтов, прочитанных в ответ, я могу просто указать это в объявлении MemoryStream и решить эту проблему. Кроме того, поскольку это относится к OP, не объявляйте MemoryStream, пока не закончите чтение.

byte[] msg = new byte[4096];
int bytesRead = someStreamClass.Read(msg, 0, 4096);
using (MemoryStream ms = new MemoryStream(msg, 0, bytesRead))
{    
    logData = Serializer.Deserialize<LogData>(ms);
}

@MarcGravell: Спасибо за эту замечательную библиотеку!