【问题标题】:WCF Streaming Convert Byte[] to Class ObjectWCF 流将字节 [] 转换为类对象
【发布时间】:2015-08-05 17:32:53
【问题描述】:

我有一个流式 WCF 服务。它接收一个名为ContentObjectData 的序列化类的流。我从流中接收到的字节暂时放在ArrayList 中,因为我不知道Stream 有多大,老实说,我也不知道如何处理它们。

ContentObjectData 类:

[Serializable]
    public class ContentObjectData
    {
     string Hash { get; set; }
     string Data { get; set; }
     string FileName { get; set; }
     string DisplayImage { get; set; }
    }

这是从客户端接收流的服务方法。

[OperationContract]
        public void SendContentObject(Stream data)
        {
            ArrayList alBytes = new ArrayList();
            Console.WriteLine("Client connected");
            int count;
            byte[] buffer = new byte[4096];
            while ((count = data.Read(buffer, 0, buffer.Length)) > 0)
            {
                alBytes.AddRange(buffer);
            }
            long i = alBytes.Count;
            data.Close();
        }

此时,我正在使用以下方法发送图像进行测试:

 private void btnLoadImage_Click(object sender, EventArgs e)
        {
            DialogResult dr = OFD.ShowDialog();
            if (dr == DialogResult.OK)
            {
                foreach (string filename in OFD.FileNames)
                {
                    try
                    {
                        ContentObject co = new ContentObject();

                        co.Data = LoadFile(filename);
                        co.Hash = Hashing.HashString(co.Data);
                        co.DisplayImage = co.Data;
                        co.FileName = co.Hash;
                        Stream stream = SerializeToStream(co);                        
                        SendContentObject(stream);
                    }
                    catch (Exception ex)
                    {
                        throw ex;
                    }

                }
            }
        }

        private void SendContentObject(Stream stream)
        {
            NetTcpBinding binding = new NetTcpBinding(SecurityMode.None, false);
            // TimeSpan.MaxValue is interpreted with a rounding error, so use 24 days instead
            binding.SendTimeout = TimeSpan.FromDays(24);
            binding.TransferMode = TransferMode.Streamed;
            ChannelFactory<RaptorStreamingHost> factory = new ChannelFactory<RaptorStreamingHost>(
                binding, new EndpointAddress("net.tcp://ccs-labs.com:804/"));
            RaptorStreamingHost service = factory.CreateChannel();
            service.SendContentObject(stream);
            ((IClientChannel)service).Close();
        }

        private string LoadFile(string filename)
        {
            return Hashing.BytesToString(File.ReadAllBytes(filename));
        }


        public static Stream SerializeToStream(object objectType)
        {
            MemoryStream stream = new MemoryStream();
            IFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, objectType);
            stream.Position = 0L; // REMEMBER to reset stream or WCF will just send the stream from the end resulting in an empty stream!
            return (Stream)stream;
        }

我有这个要反序列化,但对我来说没有多大意义:

public static object DeserializeFromStream(MemoryStream stream)
        {
            IFormatter formatter = new BinaryFormatter();
            stream.Seek(0, SeekOrigin.Begin);
            object objectType = formatter.Deserialize(stream);
            return objectType;
        }

如何将 ArrayList of Bytes(我猜是 DeSerialize 它们)转换为 New ContentObject?

更新一个 啊好近! 这个方法可以的

public static ContentObjectData DeserializeFromStream(MemoryStream stream)
        {
            IFormatter formatter = new BinaryFormatter();
            stream.Seek(0, SeekOrigin.Begin);
            ContentObjectData objectType = (ContentObjectData)formatter.Deserialize(stream);
            return objectType;
        }

我有问题。解串器找不到ContentObjectData,因为它正在客户端的命名空间中查找ContentObjectData,而不是此主机的命名空间。

【问题讨论】:

  • 你的对象 'objectType' 是什么类型(令人困惑的名称,它不是类型,而是某种类型的实例)。我打赌这是你的 ArrayList 。只是尝试施放它。 var arrayList = (ArrayList)objectType;
  • 类型是ContentObjectData
  • 但是你说你序列化了一个 ArrayList ,在你想要得到一个 ContentObjectData 之后,你确定吗?
  • 否 - 我序列化了 ContentObjectData 并将其作为流发送。在接收流时 - 我将字节保存到数组列表中

标签: c# wcf serialization streaming deserialization


【解决方案1】:

首先,不要使用ArrayList 来存储您的字节。由于ArrayList 是非通用的,每个单独的字节将是boxed 和一个指向保存在数组中的字节的指针,这将使用5(32 位)或9(64 位)倍于所需的内存。

相反,您可以将Stream 复制到本地MemoryStream,然后保存底层byte [] 数组:

    public static void CopyStream(Stream input, Stream output)
    {
        byte[] buffer = new byte[32768];
        int read;
        while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
        {
            output.Write(buffer, 0, read);
        }        
    }

    [OperationContract]
    public void SendContentObject(Stream data)
    {
        Console.WriteLine("Client connected");
        var memoryStream = new MemoryStream();
        using (data)
            CopyStream(data, memoryStream);
        byte [] alBytes = memoryStream.ToArray();
    }

然后你可以将byte []数组转回MemoryStream进行反序列化:

    public static object DeserializeFromStream(byte [] allBytes)
    {
        using (var stream = new MemoryStream(allBytes))
            return DeserializeFromStream(stream);
    }

    public static object DeserializeFromStream(MemoryStream stream)
    {
        IFormatter formatter = new BinaryFormatter();
        stream.Seek(0, SeekOrigin.Begin);
        object objectType = formatter.Deserialize(stream);
        return objectType;
    }

(或者,保留原来的MemoryStream 并传递它。)

更新

BinaryFormatter 将完整的 .Net 类型信息(即fully qualified type name)序列化到序列化流和从序列化流中提取出来。如果接收系统没有完全相同名称的类型,在完全相同的命名空间中,在完全相同的程序集中,则反序列化将失败。

如果这是您的情况,您有以下解决方法:

  1. 提取包含相关类型的共享 DLL 并将其链接到客户端和服务器;问题解决了。

  2. 写一个SerializationBinder 来映射类型程序集和名称。请参阅hereherehere 了解如何操作。

  3. 考虑一种不同的、更面向合同的二进制格式。 Bson 是一种选择。 protobuf-net 是另一个,尽管我没有使用过。更多here

【讨论】:

  • 添加了一个更新 - 你几乎可以正常工作了,谢谢!!
猜你喜欢
  • 2011-09-19
  • 1970-01-01
  • 1970-01-01
  • 2018-02-14
  • 2018-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多