简体   繁体   English

反序列化列表 <T> 通过网络流问题

[英]Deserializing List<T> over networkstream issue

Hi I am trying to send and receive some data between client/server application. 嗨,我正在尝试在客户端/服务器应用程序之间发送和接收一些数据。

Class

[Serializable]
public class ScanSessie
{
    public string UserId { get; set; }
    public int TotalScanned { get; set; }
    public string Status { get; set; }
    public string DeviceId { get; set; }
}

Serializer and Deserializer extension methods: 序列化器和反序列化器扩展方法:

public static class SerializerDeserializerExtensions
{
    public static byte[] Serializer(this object _object)
    {
        byte[] bytes;
        using (var _MemoryStream = new MemoryStream())
        {
            IFormatter _BinaryFormatter = new BinaryFormatter();
            _BinaryFormatter.Serialize(_MemoryStream, _object);
            bytes = _MemoryStream.ToArray();
        }
        return bytes;
    }

    public static T Deserializer<T>(this byte[] _byteArray)
    {
        T ReturnValue;

        using (var _MemoryStream = new MemoryStream(_byteArray))
        {
            IFormatter _BinaryFormatter = new BinaryFormatter();
            ReturnValue = (T)_BinaryFormatter.Deserialize(_MemoryStream);
        }
        return ReturnValue;
    }
}

The example data I am trying to send and deserialize it is: 我尝试发送和反序列化的示例数据是:

    List<ScanSessie> scannerCl = new List<ScanSessie>();
    scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 });
    scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 });
    scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 });
    scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 });
    scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 });
    scannerCl.Add(new ScanSessie { DeviceId = "0x00456321", UserId = "123456", Status = "scannen ...", TotalScanned = 0 });

Sending with the following code: 发送以下代码:

        NetworkStream broadcastStream = statusSocket.GetStream();

        Byte[] broadcastBytes = SerializerDeserializerExtensions.Serializer(scannerCl);

        broadcastStream.Write(broadcastBytes, 0, broadcastBytes.Length);
        broadcastStream.Flush();

Receiving stream code. 接收流代码。 ignore the n.DataAvailable loop. 忽略n.DataAvailable循环。 I am sending for test purposes very small packets way below the buffer that is transfered in one part. 为了测试目的,我在传输的缓冲区下面发送了非常小的数据包。

 using (TcpClient client = new TcpClient())
                {
                    await client.ConnectAsync("10.184.32.39", 8888);

                    ConnectionEstabilished?.Invoke();

                    byte[] message = new byte[1024 * 8];
                    int bytesRead;

                    using (NetworkStream n = client.GetStream())
                    {
                        while (true)
                        {
                            bytesRead = 0;

                            try
                            {
                                if(n.CanRead)
                                {
                                    do
                                    {
                                        bytesRead = await n.ReadAsync(message, 0, message.Length);
                                    }
                                    while (n.DataAvailable);
                                }
                                //bytesRead = await n.ReadAsync(message, 0, 4096);
                                //bytesRead = await n.ReadAsync(message, 0, message.Length);
                            }
                            catch (Exception ex)
                            {
                                // some error hapens here catch it          
                                Debug.WriteLine("DashboardClientConnect " + ex.Message + "\n" + ex.InnerException);
                                break;
                            }
                         }

The receiving code will fire an event with the data as byte[] defined as message. 接收代码将触发一个事件,其中数据作为字节[]定义为消息。 On my processing event I try to deserialize it with: 在处理事件中,我尝试使用以下方法反序列化:

private void Sc_NewDataReceived(byte[] scanner)
{
    try
    {
        var result = scanner.Deserializer<List<ScanSessie>>();
    }
    catch(Exception ex)
    {
        Debug.WriteLine(ex.InnerException);
    }
}

On the deserialize step it will throw an exception (Exception thrown: 'System.Runtime.Serialization.SerializationException' in mscorlib.dll) Innerexception is null. 在反序列化步骤中,它将引发异常(mscorlib.dll中引发的异常:'System.Runtime.Serialization.SerializationException')Innerexception为null。

When I use the extension methods without sending it over network with some example data the deserializing works flawless. 当我使用扩展方法而不通过网络将其与一些示例数据一起发送时,反序列化工作就完美了。

I've also tried to play around with the receiving buffer sizes. 我也尝试过尝试接收缓冲区的大小。 That doesn't seems to help also. 这似乎也无济于事。 The example data size is below 1024*8. 示例数据大小低于1024 * 8。

If the send data length is 600 bytes for example. 例如,如果发送数据长度为600字节。 Does the receiving end buffer also needs to be the same size? 接收端缓冲区是否也需要相同大小? Normally that should be a problem because it reads in chunks. 通常,这应该是一个问题,因为它会分块读取。

After 2 days I give up. 2天后,我放弃了。 Any help would be appreciated. 任何帮助,将不胜感激。 I tried to make the question informative by placing the functioning code snippets. 我试图通过放置功能正常的代码片段来使问题更有意义。

This code has several problems: 这段代码有几个问题:

 do
 {
     bytesRead = await n.ReadAsync(message, 0, message.Length);
 }
 while (n.DataAvailable);

First, sender might send message bytes in chunks. 首先,发件人可能会分块发送消息字节。 First you will read one chunk, then you will read another chunk overwriting first one in buffer (because you use the same buffer for all reads, and you always write to index 0). 首先,您将读取一个块,然后读取另一个块,该块将覆盖缓冲区中的第一个块(因为您对所有读取使用相同的缓冲区,并且始终写入索引0)。

Also, you have no idea when message was really received completely. 另外,您不知道何时真正收到邮件。 Sender might send one chunk, then there is a delay for whatever reason, at this point your while (n.DataAvailable) loop exists and you are trying to deserialize incomplete message. 发件人可能会发送一个块,然后由于某种原因会有延迟,这时您的while (n.DataAvailable)循环存在,并且您正在尝试反序列化不完整的消息。

On the top of it - when you were lucky to receive full message - the rest of the buffer (assuming message length is smaller that buffer size) is filled with zeroes which anyway prevents successful deserialization. 最重要的是-当您幸运地收到完整的消息时-缓冲区的其余部分(假设消息长度小于缓冲区大小)将填充为零,这始终会阻止反序列化成功。

Also - it's not a good idea to use BinaryFormatter to serialize objects over the network. 另外-使用BinaryFormatter在网络上序列化对象不是一个好主意。 Instead use something like protobuf. 而是使用protobuf之类的东西。

To fix your networking code - first send length of your serialized message. 要修复您的网络代码-首先发送序列化消息的长度。 If you have different message types - also send message type. 如果您有不同的消息类型-还要发送消息类型。 After sending length (and type if necessary) - send message itself: 发送长度后(如有必要,请键入)-发送消息本身:

NetworkStream broadcastStream = statusSocket.GetStream();
Byte[] broadcastBytes = SerializerDeserializerExtensions.Serializer(scannerCl);
var lengthBytes = BitConverter.GetBytes(broadcastBytes.Length);
if (!BitConverter.IsLittleEndian)
     Array.Reverse(lengthBytes);
broadcastStream.Write(lengthBytes, 0, lengthBytes.Length);
broadcastStream.Write(broadcastBytes, 0, broadcastBytes.Length);
broadcastStream.Flush();

Then on receiving side: 然后在接收端:

byte[] lengthBuffer = new byte[sizeof(int)];
var bytesRead = await n.ReadAsync(lengthBuffer, 0, lengthBuffer.Length);
// check if bytesRead equals buffer size
if (!BitConverter.IsLittleEndian)
    Array.Reverse(lengthBuffer);
var length = BitConverter.ToInt32(lengthBuffer, 0);
// check if length is not too big, otherwise you will crash process with out of memory
var messageBuffer = new byte[length];
bytesRead = await n.ReadAsync(messageBuffer, 0, messageBuffer.Length);
// check if bytesRead equals buffer size
// now you can deserialize

Note that this code is untested, so take care. 请注意,此代码未经测试,因此请小心。

I suggest to use WCF, .NET Remoting or something comparable. 我建议使用WCF,.NET Remoting或类似产品。 They are designed for exactly that purpose. 它们正是为此目的而设计的。

If you write your own transmission channel, normally you have to embedd the serialized data in a data communication protocol. 如果编写自己的传输通道,通常必须将序列化的数据嵌入数据通信协议中。 Typically this contains a message length and other information about the transmitted data. 通常,它包含消息长度和有关已传输数据的其他信息。 This is required, eg to let the receiver know the data/message size and data type. 这是必需的,例如,让接收者知道数据/消息的大小和数据类型。 On the receiver side, normally you have no idea about, how many bytes have to be received and what they are. 在接收方,通常您不知道要接收多少字节以及它们是多少。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM