【问题标题】:Random serialization exceptions using Socket使用 Socket 的随机序列化异常
【发布时间】:2013-11-05 04:06:14
【问题描述】:

我正在尝试使用 Socket 仅在两台计算机之间来回发送数据。数据是序列化的Packet对象的形式。

在本地网络上的另一台计算机上测试程序时,我得到随机的 SerializationExceptions,因此没有数据通过。

程序始终发送不同的数据,所以当它再次发送时,它有时会通过,有时会再次遇到相同的 SerializationException。如果我捕捉到异常并让它继续运行,所有数据最终都会通过,但需要多次尝试。

异常说:“输入流不是有效的二进制格式。起始内容(以字节为单位)是[字节数据]”

不确定我的问题到底出在哪里。我发送的大量数据(最大约 100kb)总是通过。较小的(50-70 字节)有问题。这是与我的序列化和读/写数据有关的所有内容。

Socket 定义如下:

SocketMain = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);

发送和读取方法。我知道这可能是一种可怕的方式,并且可能最终成为我的问题。建议?:

    public void SendPacket(Packet P)
    {
            using (MemoryStream MS = new MemoryStream())
            {
                BinaryFormatter BF = new BinaryFormatter();
                BF.Serialize(MS, P);
                SocketMain.Send(MS.ToArray());
            }
    }

    public void ReadPacket()
    {
        byte[] BufferArray = new byte[131072];
        int BytesReceived = SocketMain.Receive(BufferArray);

        byte[] ActualData = new byte[BytesReceived];
        Buffer.BlockCopy(BufferArray, 0, ActualData, 0, BytesReceived);

        using (MemoryStream MS = new MemoryStream(ActualData))
        {
            BinaryFormatter BF = new BinaryFormatter();
            HandlePacket((Packet)BF.Deserialize(MS));
        }
    }

示例数据包对象。这是我的较小的之一。我认为这可能是导致问题的原因,但我不知道如何判断。

[Serializable()]
public class Packet4BlockVerify : Packet, ISerializable
{
    public byte Index;
    public string MD5Hash;

    public Packet4BlockVerify(int Index, string MD5Hash): base(4)
    {
        this.Index = (byte)Index;
        this.MD5Hash = MD5Hash;
    }

    protected Packet4BlockVerify(SerializationInfo info, StreamingContext context)
    {
        this.ID = info.GetByte("ID");
        this.Index = info.GetByte("Index");
        this.MD5Hash = info.GetString("MD5Hash");
    }

    public override void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        info.AddValue("ID", this.ID);
        info.AddValue("Index", this.Index);
        info.AddValue("MD5Hash", this.MD5Hash);
    }
}

有人发现有什么不对吗?

【问题讨论】:

  • 您的线程中的 socket.send/receive 行在哪里?我通过在数据包中添加页眉和页脚然后在接收器部分读取网络直到找到标头来处理本地网络中的发送/接收数据。
  • 我只使用本地计算机进行测试。该程序旨在通过互联网使用。数据包的发送和接收大部分都在同一个线程上结束,当收到一个数据包时,它会立即通过发送另一个适当的数据包来响应。
  • 我没有看到您发布的代码有任何特别说明,但认为您的逻辑不够棘手。有一些标准的网络编程示例可能会对您有所帮助。对于您的问题,请提供完整的源代码需要重现它的错误。
  • 我已经编辑了你的标题。请参阅“Should questions include “tags” in their titles?”,其中的共识是“不,他们不应该”。

标签: c# sockets serialization


【解决方案1】:

您没有读取您发送的所有字节。您的接听电话:

int BytesReceived = SocketMain.Receive(BufferArray);

返回任意数量的字节。您需要在发送的字节前面加上剩余字节的大小,然后继续读取,直到您拥有所有字节,然后再尝试反序列化。

TCP 发送一个连续的字节流,因此您的接收调用读取任意大小的块。您可以指定要接收的字节数之一,因此在读取您期望的字节数后,您可以使用它。例如

// Warning untested! (but you get the idea)

// when sending
var payload = MS.ToArray();
var payloadSize = payload.Length;
mySocket.Send(BitConverter.GetBytes(payloadSize));
mySocket.Send(payload);

// when recieving
mySocket.Recieve(myBuffer, sizeof(int), SocketFlags.None);
var payloadSize = BitConverter.ToInt32(myBuffer, 0);
mySocket.Recieve(myBuffer, payloadSize, SocketFlags.None);
// now myBuffer from index 0 - payloadSize contains the payload you sent

【讨论】:

  • 看起来你是对的。我使用控制台记录了发送和接收的数量,但我没有得到全部内容。不过,我不确定我将如何去做你所说的。可以举个例子吗?
  • 有道理。序列化仍然会出现一些奇怪(不同)的错误,但它现在确实工作得更好。
  • 有什么异常?
  • 经过几次更改后停止工作。我收到 SerializationException:“二进制流 '0' 不包含有效的 BinaryHeader。”在发送了几个数据包之后。现在我得到的数据长度与我发送的相同,但实际数据不同。 MD5 哈希值不同。
  • 在反序列化时记住只使用缓冲区中的 x 个字节数,而不是整个缓冲区,可以吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-18
  • 2012-05-10
  • 2019-05-05
  • 1970-01-01
  • 2020-05-02
  • 2012-11-28
相关资源
最近更新 更多