【问题标题】:Send/Receive GZip compressed MSMQ messages in C#在 C# 中发送/接收 GZip 压缩的 MSMQ 消息
【发布时间】:2011-08-29 05:29:33
【问题描述】:

我正在尝试将大型对象 (>30MB) 发送到 MSMQ 队列。由于我们要发送的数据量很大,因此我们的想法是在发送对象之前先对对象进行 GZip,然后在接收端解压缩。

但是,将压缩流写入message.BodyStream 属性似乎可行,但不能从那里读取。 不知道怎么回事。

Message l_QueueMessage = new Message();
l_QueueMessage.Priority = priority;

using (MessageQueue l_Queue = CreateQueue())
{
    GZipStream stream = new GZipStream(l_QueueMessage.BodyStream, CompressionMode.Compress);

    Formatter.Serialize(stream, message);

    l_Queue.Send(l_QueueMessage);
}

Formatter 是 BinaryFormatter 类型的全局属性。这用于序列化/反序列化为我们想要发送/接收的对象类型,例如“产品项目”。

接收端是这样的:

GZipStream stream = new GZipStream(l_Message.BodyStream, CompressionMode.Decompress);

object decompressedObject = Formatter.Deserialize(stream);

ProductItem l_Item = decompressedObject as ProductItem;

m_ProductReceived(sender, new MessageReceivedEventArgs<ProductItem>(l_Item));

l_ProductQueue.BeginReceive();

我收到一个 EndOfStreamException "{"Unable to read beyond the end of the stream."} 试图反序列化 在 System.IO.BinaryReader.ReadByte()

使用 messageBodyStream 属性,我实际上绕过了 message.Formatter,我没有初始化任何东西,因为我在 GZipStream 中使用了我自己的 ser/deser 机制。但是,我不确定这是否是正确的做法。

我错过了什么? 谢谢!

【问题讨论】:

    标签: c# msmq iostream binary-data gzipstream


    【解决方案1】:

    在您的原始代码中,问题是您需要关闭GZipStream 才能正确编写 GZip 页脚,然后才能发送它。如果你不这样做,你最终会发送无法反序列化的字节。这也是为什么稍后发送的新代码可以工作的原因。

    【讨论】:

    • 非常感谢您的观察。我想知道是否需要打开/关闭 gzip 流以及这是否会影响解压缩。无论哪种情况,我都想知道为什么 - 使用我的旧接收代码 - 即使使用我上面发布的新发件人代码也无法正常工作。
    • 我可以确认,即使我现在在发送之前正确关闭了 gzip 流,我仍然无法反序列化。
    • 解决方案的主要部分仍然在接收者的代码中。 GZipStream 在发送我的示例之前也已关闭,正如 John 所说,它并没有真正起作用。
    【解决方案2】:

    好的,我完成了这项工作。关键是将接收器上的解压缩流转换为 byte[] 数组。然后反序列化开始工作。

    发送方代码(注意发送消息前已关闭流):

    using (MessageQueue l_Queue = CreateQueue())
    {
        using (GZipStream stream = new GZipStream(l_QueueMessage.BodyStream, CompressionMode.Compress, true))
        {
            Formatter.Serialize(stream, message);
        }
    
        l_Queue.Send(l_QueueMessage);
    }
    

    接收端(注意我如何将流转换为 byte[] 然后反序列化):

    using (GZipStream stream = new GZipStream(l_QueueMessage.BodyStream, CompressionMode.Decompress))
    {
        byte[] bytes = ReadFully(stream);
    
        using (MemoryStream ms = new MemoryStream(bytes))
        {
            decompressedObject = Formatter.Deserialize(ms);
        }
    }
    

    仍然不知道为什么使用 ReadFully() 函数而不是 Stream.CopyTo() 可以工作。 有人吗?

    顺便说一句,ReadFully() 是一个从 Stream 中创建 byte[] 的函数。我必须在http://www.yoda.arachsys.com/csharp/readbinary.html 上感谢 Jon Skeet。谢谢!

    【讨论】:

      【解决方案3】:

      尽量分开压缩和发送:

      byte[] binaryBuffer = null;
      using (MemoryStream compressedBody = new MemoryStream()) 
      {
          using(GZipStream stream = new GZipStream(compressedBody, CompressionMode.Compress))
          {
              Formatter.Serialize(compressedBody, message); 
              binaryBuffer = compressedBody.GetBuffer();
          }
      }
      
      using (MessageQueue l_Queue = CreateQueue())        
      {            
          l_QueueMessage.BodyStream.Write(binaryBuffer, 0, binaryBuffer.Length);
          l_QueueMessage.BodyStream.Seek(0, SeekOrigin.Begin);
          l_Queue.Send(l_QueueMessage);
      }
      

      【讨论】:

      • 感谢您的宝贵时间。我尝试了您建议的代码,但没有运气。显然,发送很好,但接收端无法正常工作。使用我的原始代码,我得到一个“魔术包”异常,这意味着消息正文流不是 GZip 流。 “GZip 标头中的幻数不正确。请确保您传入的是 GZip 流。”
      • GZipStrem 关闭后才能写入缓冲区。
      猜你喜欢
      • 1970-01-01
      • 2011-08-13
      • 2023-03-07
      • 1970-01-01
      • 2010-10-16
      • 2015-04-30
      • 1970-01-01
      • 2020-04-13
      • 1970-01-01
      相关资源
      最近更新 更多