簡體   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