繁体   English   中英

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

[英]Deserializing List<T> over networkstream issue

嗨,我正在尝试在客户端/服务器应用程序之间发送和接收一些数据。

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

序列化器和反序列化器扩展方法:

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;
    }
}

我尝试发送和反序列化的示例数据是:

    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 });

发送以下代码:

        NetworkStream broadcastStream = statusSocket.GetStream();

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

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

接收流代码。 忽略n.DataAvailable循环。 为了测试目的,我在传输的缓冲区下面发送了非常小的数据包。

 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;
                            }
                         }

接收代码将触发一个事件,其中数据作为字节[]定义为消息。 在处理事件中,我尝试使用以下方法反序列化:

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

在反序列化步骤中,它将引发异常(mscorlib.dll中引发的异常:'System.Runtime.Serialization.SerializationException')Innerexception为null。

当我使用扩展方法而不通过网络将其与一些示例数据一起发送时,反序列化工作就完美了。

我也尝试过尝试接收缓冲区的大小。 这似乎也无济于事。 示例数据大小低于1024 * 8。

例如,如果发送数据长度为600字节。 接收端缓冲区是否也需要相同大小? 通常,这应该是一个问题,因为它会分块读取。

2天后,我放弃了。 任何帮助,将不胜感激。 我试图通过放置功能正常的代码片段来使问题更有意义。

这段代码有几个问题:

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

首先,发件人可能会分块发送消息字节。 首先,您将读取一个块,然后读取另一个块,该块将覆盖缓冲区中的第一个块(因为您对所有读取使用相同的缓冲区,并且始终写入索引0)。

另外,您不知道何时真正收到邮件。 发件人可能会发送一个块,然后由于某种原因会有延迟,这时您的while (n.DataAvailable)循环存在,并且您正在尝试反序列化不完整的消息。

最重要的是-当您幸运地收到完整的消息时-缓冲区的其余部分(假设消息长度小于缓冲区大小)将填充为零,这始终会阻止反序列化成功。

另外-使用BinaryFormatter在网络上序列化对象不是一个好主意。 而是使用protobuf之类的东西。

要修复您的网络代码-首先发送序列化消息的长度。 如果您有不同的消息类型-还要发送消息类型。 发送长度后(如有必要,请键入)-发送消息本身:

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();

然后在接收端:

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

请注意,此代码未经测试,因此请小心。

我建议使用WCF,.NET Remoting或类似产品。 它们正是为此目的而设计的。

如果编写自己的传输通道,通常必须将序列化的数据嵌入数据通信协议中。 通常,它包含消息长度和有关已传输数据的其他信息。 这是必需的,例如,让接收者知道数据/消息的大小和数据类型。 在接收方,通常您不知道要接收多少字节以及它们是多少。

暂无
暂无

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

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