9

我有以下 C# 代码,它应该将任意对象序列化为字符串,然后当然反序列化它。

public static string Pack(Message _message)
{
   BinaryFormatter formatter = new BinaryFormatter();

   MemoryStream original = new MemoryStream();
   MemoryStream outputStream = new MemoryStream();

   formatter.Serialize(original, _message);
   original.Seek(0, SeekOrigin.Begin);

   DeflateStream deflateStream = new DeflateStream(outputStream, CompressionMode.Compress);
   original.CopyTo(deflateStream);

   byte[] bytearray = outputStream.ToArray();

   UTF8Encoding encoder = new UTF8Encoding();
   string packed = encoder.GetString(bytearray);
   return packed;
} 

public static Message Unpack(string _packed_message)
{
    UTF8Encoding encoder = new UTF8Encoding();
    byte[] bytearray = encoder.GetBytes(_packed_message);

    BinaryFormatter formatter = new BinaryFormatter();

    MemoryStream input = new MemoryStream(bytearray);
    MemoryStream decompressed = new MemoryStream();

    DeflateStream deflateStream = new DeflateStream(input, CompressionMode.Decompress);
    deflateStream.CopyTo(decompressed); // EXCEPTION

    decompressed.Seek(0, SeekOrigin.Begin);

    var message = (Message)formatter.Deserialize(decompressed); // EXCEPTION 2
    return message;
}

但问题是,无论何时运行代码,我都会遇到异常。使用上面的代码并如下所示调用它,我收到InvalidDataException: Unknown block type。流可能已损坏。在标记// EXCEPTION线上。

在搜索了这个问题之后,我试图放弃通货紧缩。这只是一个很小的变化: in Packbytearrayoriginal.ToArray()和 in 中创建Unpack,我Seek() input代替decompressed和使用Deserialize(input)代替decompressed太。唯一改变的结果:异常位置和主体不同,但它仍然发生。我收到SerializationException: No map for object '201326592'。// EXCEPTION 2.

我似乎没有看到有什么问题。也许这是整个序列化的想法……问题在于以某种方式管理实例是必要的Message,因为这些对象包含在服务器和客户端应用程序之间传输的信息。(序列化逻辑在两端引用的.Shared DLL 项目中,但是,现在,我只首先开发服务器端。)还必须告诉我,我只使用string输出,因为现在,服务器和客户端之间的TCP连接是基于字符串读写的。因此,它必须以某种方式降低到字符串的水平。

这是 Message 对象的样子:

[Serializable]
public class Message
{
    public MessageType type;
    public Client from;
    public Client to;
    public string content;
}

Client现在是一个空类,只有Serializable属性,没有属性或方法。)

这就是调用 pack-unpack 的方式(来自Main()...):

 Shared.Message msg = Shared.MessageFactory.Build(Shared.MessageType.DEFAULT, new Shared.Client(), new Shared.Client(), "foobar");

 string message1 = Shared.MessageFactory.Pack(msg);
 Console.WriteLine(message1);

 Shared.Message mess2 = Shared.MessageFactory.Unpack(message1); // Step into... here be exceptions
 Console.Write(mess2.content);

这是显示 IDE 中发生的情况的图像。控制台窗口中的输出是 的值message1显示异常发生和输出的图像

不幸的是,一些调查还显示问题可能在于bytearray变量。运行Pack()时,编码器创建字符串后,数组包含 152 个值,然而,在它被解码后Unpack(),数组有160个值。

我很感激任何帮助,因为我真的没有想法,遇到这个问题,进度就会受到影响。谢谢你。

(更新)最终解决方案:

我要感谢大家的回答和评论,因为我已经找到了解决方案。谢谢你。

Marc Gravell是对的,我错过了关闭,deflateStream因此,结果要么是空的,要么是损坏的。我花时间重新思考并重写了方法,现在它完美无缺。甚至通过网络流发送这些字节的目的也在起作用。

此外,正如Eric J.所建议的,我已切换ASCIIEncondingstring使用.byte[]Stream

固定代码如下:

public static string Pack(Message _message)
{
    using (MemoryStream input = new MemoryStream())
    {
       BinaryFormatter bformatter = new BinaryFormatter();
       bformatter.Serialize(input, _message);
       input.Seek(0, SeekOrigin.Begin);

       using (MemoryStream output = new MemoryStream())
       using (DeflateStream deflateStream = new DeflateStream(output, CompressionMode.Compress))
       {
           input.CopyTo(deflateStream);
           deflateStream.Close();

           return Convert.ToBase64String(output.ToArray());
       }
    }
}

public static Message Unpack(string _packed)
{
    using (MemoryStream input = new MemoryStream(Convert.FromBase64String(_packed)))
    using (DeflateStream deflateStream = new DeflateStream(input, CompressionMode.Decompress))
    using (MemoryStream output = new MemoryStream())
    {
        deflateStream.CopyTo(output);
        deflateStream.Close();
        output.Seek(0, SeekOrigin.Begin);

        BinaryFormatter bformatter = new BinaryFormatter();
        Message message = (Message)bformatter.Deserialize(output);
        return message;
    }
}

现在一切都发生得恰到好处,如下图所示。这是最初的预期输出。服务器客户端可执行文件相互通信并且消息传播......并且它被正确地序列化和反序列化。

显示正确输出的图像

4

3 回答 3

6

除了现有的关于 Encoding vs base-64 的观察,请注意您还没有关闭 deflate 流。这很重要,因为compression-streams buffer:如果你不关闭,它可能不会写完。对于短流,这可能意味着它什么也不写。

using(DeflateStream deflateStream = new DeflateStream(
    outputStream, CompressionMode.Compress))
{
    original.CopyTo(deflateStream);
}
return Convert.ToBase64String(outputStream.GetBuffer(), 0,
    (int)outputStream.Length);
于 2012-12-02T22:34:47.867 回答
2

您的问题很可能出在 UTF8 编码中。您的字节并不是真正的字符串,UTF-8 是一种具有不同字节长度的字符编码。这意味着字节数组可能与正确编码的 UTF-8 字符串不对应(例如,末尾可能缺少一些字节。)

尝试使用恒定长度编码的 UTF16 或 ASCII(生成的字符串可能包含控制字符,因此无法通过 HTTP 或电子邮件等方式打印或传输。)

但是如果你想编码为字符串,习惯上使用 UUEncoding 将字节数组转换为真正的可打印字符串,那么你可以使用任何你想要的编码。

于 2012-12-02T22:15:39.427 回答
2

当我对您的 Pack() 和 Unpack() 运行以下 Main() 代码时:

    static void Main(string[] args)
    {
        Message msg = new Message() { content = "The quick brown fox" };

        string message1 = Pack(msg);
        Console.WriteLine(message1);

        Message mess2 = Unpack(message1); // Step into... here be exceptions
        Console.Write(mess2.content);
    }

我看到字节数组

byte[] bytearray = outputStream.ToArray();

是空的。

我确实稍微修改了您的序列化类,因为您没有发布包含类的代码

public enum MessageType
{
    DEFAULT = 0
}

[Serializable]
public class Message
{
    public MessageType type;
    public string from;
    public string to;
    public string content;
}

我建议采取以下步骤来解决这个问题:

  • 一路检查中间结果。您还看到数组中的 0 个字节吗?Pack() 返回的字符串值是多少?
  • 处理完您的流后处理它们。最简单的方法是使用using关键字。

编辑

正如 Eli 和 Marc 正确指出的那样,您不能在 UTF8 字符串中存储任意字节。映射不是双射的(你不能在没有信息丢失/失真的情况下来回走动)。您将需要一个双射映射,例如 Marc 建议的 Convert.ToBase64String() 方法。

于 2012-12-02T22:24:41.837 回答