通过 TCP 发送大对象:“在解析完成之前遇到流结束" [英] Sending large objects over TCP: "End of Stream encountered before parsing was completed"

查看:27
本文介绍了通过 TCP 发送大对象:“在解析完成之前遇到流结束"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

每当我尝试从 NetworkStream 反序列化双精度列表时,我都会收到 SerializationException:

I keep getting an SerializationException whenever I try to deserialize a list of doubles from a NetworkStream:

之前遇到的流结束解析完成

End of Stream encountered before parsing was completed

我有一个简单的客户端服务器架构:我的 TcpTransportClient 包装了 TcpClient 的功能,我使用了两种基本方法:Send(发送一条消息)和 Receive(阻塞直到收到一条消息).

I have a simple client server architecture: my TcpTransportClient wraps the functionality of a TcpClient and I utilize two basic methods: Send (sends a message) and Receive (blocks until a message is received).

Send 函数接受一个 Message,使用 BinaryFormatter 将其序列化并通过 NetworkStream 发送字节.

The Send function takes a Message, serializes it using a BinaryFormatter and sends the bytes via the NetworkStream.

public void Send(Message message)
{
    if (message == null)
    {
        throw new TransportException("Invalidate Parameter In Send Call");
    }

    if (_tcpClient == null || !_tcpClient.Connected)
    {
        throw new TransportException("Client Not Connected");
    }

    lock (_sync)
    {
        // Serialzie the message
        MemoryStream memStream = new MemoryStream();
        _serializer.Serialize(memStream, message);

        // Get the bytes of of the memory stream
        byte[] buffer = memStream.GetBuffer();

        // Write the message to the network stream
        NetworkStream networkStream = _tcpClient.GetStream();
        networkStream.Write(buffer, 0, buffer.Length);
        networkStream.Flush();
    }
}

接收函数从 NetworkStream 读取字节到缓冲区,然后使用 BinaryFormatter 反序列化消息:

The receive function reads bytes into a buffer from the NetworkStream, then deserializes the message by using a BinaryFormatter:

public Message Receive()
{
    if (_tcpClient == null || !_tcpClient.Connected)
    {
        throw new TransportException("Client Not Connected");
    }

    byte[] buffer;
    MemoryStream memStream = new MemoryStream();
    NetworkStream netStream = _tcpClient.GetStream();
    try
    {
        do
        {
            // Allocate a new buffer
            buffer = new byte[_tcpClient.ReceiveBufferSize];

            // Read the message buffer
            int sizeRead = netStream.Read(buffer, 0, buffer.Length);

            // Write the buffer to the memory stream
            memStream.Write(buffer, 0, sizeRead);

        } while (netStream.DataAvailable);

        // Reset the memory stream position
        memStream.Position = 0;

        // Deserialize the message
        object tmp = _deserializer.Deserialize(memStream); // <-- The exception is here!

        // Cast the object to a message
        return (Message)tmp;
    }
    catch (System.Exception e)
    {
        if (_tcpClient == null || !_tcpClient.Connected)
        {
            throw new TransportException("Client Not Connected");
        }
        else
        {
            throw e;
        }
    }
}

我有一个基本的发送线程:

I have a basic sending thread:

TcpTransportClient client = new TcpTransportClient(GetLocalAddress(), servicePort);
client.Connect();

Thread sendThread = new Thread(() =>
{
    List<double> dataSet = new List<double>();
    for (double d = 0.0; d < 500.0; d++)
    {
        dataSet.Add(d);
    }

    while (true)
    {
        try
        {
            // Serialize the data set
            MemoryStream memStream = new MemoryStream();
            BinaryFormatter binFormat = new BinaryFormatter();
            binFormat.Serialize(memStream, (object)dataSet);

            // Create a message
            Message msg = new Message();

            // Hold the object bytes in an opaque field
            msg.SetFieldValue(1000, memStream.GetBuffer());

            // Set the message topic
            msg.SetTopic("client.dataSet");

            // Send the message
            client.Send(msg);

            // Sleep
            Thread.Sleep(3000);
        }
        catch (TransportException)
        {
            break;
        }
        catch(Exception)
        {
            //ignore it
        }
    }
});
sendThread.IsBackground = true;
sendThread.Start();

还有一个接收线程,每当 TcpClient 被接受时就会启动:

And a receive thread which gets started whenever a TcpClient is accepted:

public void HandleAcceptedClient(TcpTransportClient client)
{
    Thread receiveThread = new Thread(() =>
    {
        while (true)
        {
            try
            {
                Message msg = client.Receive();
                Trace.WriteLine("Server Received: " + msg.GetTopic());
                byte[] buffer = msg.GetFieldOpaqueValue(1000);
                MemoryStream memStream = new MemoryStream(buffer);
                BinaryFormatter binFormat = new BinaryFormatter();

                List<double> dataSet = (List<double>)binFormat.Deserialize(memStream);
                if (dataSet.Count == 500)
                {
                    Trace.WriteLine(DateTime.Now + ": OK");
                }
                else
                {
                    Trace.WriteLine(DateTime.Now + ": FAIL");
                }
            }
            catch (TransportException)
            {
                break;
            }
            catch(Exception)
            {
                // ignore it
            }

        }

    });
    receiveThread.IsBackground = true;
    receiveThread.Start();
}

当我尝试在我的 TcpTransportClientReceive 方法中反序列化消息时,总是会发生异常,但只有在我将一些数据放入数据集中时才会出现问题.通过网络发送值列表并在接收端成功反序列化它们的正确方法是什么?

The exception always occurs when I try to deserialize the message in Receive method of my TcpTransportClient, but the problem only occurs if I put some data in the data set. What's the proper way to send a list of values over a network and successfully deserialize them on the receiving end?

附言我在几乎相同的问题中尝试了该解决方案,但它没有用:我仍然遇到同样的异常.

P.S. I tried the solution in a nearly identical question, but it didn't work: I'm still getting the same exception.

推荐答案

while (netStream.DataAvailable);

while (netStream.DataAvailable);

这是不对的.当 Read() 调用返回 0 时,您应该停止读取.DataAvailable 属性只是告诉您 Read() 调用是否会阻塞,等待服务器跟上.

That's not correct. You should stop reading when the Read() call returns 0. The DataAvailable property just tells you whether or not the Read() call will block, waiting to allow the server to catch up.

这篇关于通过 TCP 发送大对象:“在解析完成之前遇到流结束"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆