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ラインで。

この問題を検索した後、私はデフレを捨てようとしました。これは小さな変更Packに過ぎませbytearrayoriginal.ToArray()でしUnpackた。変更された唯一の結果: 例外の位置と本体は異なりますが、それでも発生します。SerializationException: No map for object '201326592'を受け取りました。で。Seek() inputdecompressedDeserialize(input)decompressed// EXCEPTION 2

何が問題なのかわかりません。多分それはシリアライゼーション全体のアイデアです...問題は、これらのオブジェクトがサーバーとクライアントアプリケーションの間を移動する情報を保持するため、何らかの方法でインスタンスをパックする必要があることです. Message(シリアライゼーション ロジックは、両端で参照される.Sharedstring DLL プロジェクトにありますが、現時点では、最初にサーバー側のみを開発しています。) また、現在は出力のみを使用していることも伝えなければなりません。 、サーバーとクライアント間の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.が示唆したように、とASCIIEncondingの間stringbyte[]データが流れているときに を使用するように切り替えました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 と base-64 に関する既存の観察に加えて、deflate ストリームを閉じていないことに注意してください。これは重要です。なぜなら、圧縮ストリーム バッファ: 閉じないと、末尾を書き込めない可能性があるからです。短いストリームの場合、何も書き込まないことを意味する場合があります。

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 に答える