通过TCP发送多个对象而不关闭或处置流

时间:2013-10-25 10:56:36

标签: c# tcp

我使用BinaryFormatter通过NetworkStream序列化对象 像这样的代码

//OpenConnection ...
TCPClient client = server.AcceptTCPConnection();
Message message = new Message("bla bla"); // This is the serializable class
NetworkStream stream = client.GetStream(); // Get Stream
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(stream, message);
stream.Flush();
stream.Close(); //Close Connection

在客户端代码中,我们只需要从流中读取 bf.Deserialize(stream) as Message 获取我们刚从Server发送的对象。

但是这里有一个问题,如果我删除了行stream.Close();,则客户端无法读取此对象。或者我可以更改为stream.Dispose();

但是,我想再次使用此流来发送另一个Message,我该怎么办?请帮助,这让我感到头疼@@

更新 我找到了这个问题的原因。因为我使用一台机器来运行客户端和服务器。它绝对适用于两台不同的机器。有人可以告诉我为什么?几天前就遇到了大问题。

1 个答案:

答案 0 :(得分:3)

发送多个单独的消息涉及“成帧” - 将单个通道拆分为单独的块,这些块不需要客户端“读取结束”。但奇怪的是,我的印象是BinaryFormatter 已经实施了基本框架 - 但是:我可能错了。在一般情况下,使用二进制协议时,最常见的方法是在每条消息前面加上有效负载的长度,即

using(var ms = new MemoryStream()) {
    while(...)
    {
        // not shown: serialize to ms

        var len BitConverter.GetBytes((int)ms.Length);
        output.Write(len, 0, 4);
        output.Write(ms.GetBuffer(), 0, (int) ms.Length);
        ms.SetLength(0); // ready for next cycle
    }
}

来电者必须:

  • 正好读取4个字节(至少对于上面的内容),或者检测EOF
  • 确定长度
  • 准确读取 多字节
  • 反序列化
  • 重复

如果这听起来像是很多工作,也许只需使用一个为你完成所有这些工作的序列化工具;例如,使用protobuf-net,这将是:

while(...) { // each item
    Serializer.SerializeWithLengthPrefix(output, PrefixStyle.Base128, 1);
}

读者将是:

foreach(var msg in Serializer.DeserializeItems<Message>(
       input, PrefixStyle.Base128, 1))
{
   // ...
}

(注意:这不使用与BinaryFormatter相同的格式/规则)