C#在同一个Socket上发送多个对象

时间:2011-04-11 16:40:24

标签: c# sockets serialization

我正在尝试从一个套接字向另一个套接字发送多个不同的对象(好吧,它们是相同的对象类型,只是具有不同的值)。我可以使用以下代码发送一个对象:

客户端:

var buffer = new byte[1024];
var binFormatter = new BinaryFormatter();
using (var ms = new MemoryStream())
{
    int bytesRead;
    while ((bytesRead = _clientReceive.Receive(buffer)) > 0);
    {
        ms.Write(buffer, 0, bytesRead);
    } 

    ms.Position = 0;
    message = (Message)binFormatter.Deserialize(ms);
} 

服务器:

var gm = new Message { Message = "ObjectType", Object = Data };

using (var mem = new MemoryStream())
{
    var binFormatter = new BinaryFormatter();
    binFormatter.Serialize(mem, gm);
    var gmData = mem.ToArray();
    client.Send(gmData, gmData.Length, SocketFlags.None);
    client.Close();
}

但如果我想发送多条消息(将完整的客户端代码放在循环中而不是调用client.Close()),我怎样才能确定客户端何时收到完整的对象?将客户端代码放在这样的循环中:

while (_isReceivingStarted)
{
    var buffer = new byte[1024];
    var binFormatter = new BinaryFormatter();
    using (var ms = new MemoryStream())
    {
        int bytesRead;
        while ((bytesRead = _clientReceive.Receive(buffer)) > 0)
        {
            ms.Write(buffer, 0, bytesRead);
        }
        ms.Position = 0;
        message = (Message) binFormatter.Deserialize(ms);
    }
    // Do stuff then wait for new message
}

客户端将挂起_clientReceive.Receive(buffer),因为它没有从客户端收到Close()并且永远不会得到0个接收的字节。如果我关闭服务器上的连接,它将循环遍历,然后在Deserialize MemoryStream中出错,而不是在_clientReceive.Receive(buffer)阻止预期另一个对象被发送。

我希望这是有道理的。有什么指针吗?

5 个答案:

答案 0 :(得分:3)

我强烈建议您查看Windows Communication Foundation。它将为您处理所有这些管道,包括通过线路序列化和反序列化您的类型,通过多个可配置的通道等。

答案 1 :(得分:3)

当使用这样的套接字时,我会使用protobuf-net而不是BinaryFormatter(警告/披露:我写了它)特别是,如果你每个项目Serializer.SerializeWithLenthPrefix(...)使用Base128作为前缀样式(其中一个选项),使用一些已知标记(另一个选项),例如1,然后在另一端使用DeserializeItems<Foo>(...) (再次指定Base128和1)。

这将正确地选择项目,而不会尝试过度阅读,直到该流关闭,时间为yield break。每个对象都会单独返回(yield return),因此在向您提供数据之前,不会尝试使用整个流。

如果您愿意,它也可以与异构数据一起使用,但同类最简单。

答案 2 :(得分:1)

您可能会发送一条标头消息,通知接收端需要多少字节。这类似于HTTP中的Content-Length指令。其他选项,制作一个你最后发送的自定义终止字符串(显然它不会在序列化对象的二进制有效负载中的任何位置出现,这就是为什么前一个解决方案就是我要做的)。

答案 3 :(得分:0)

查看TcpListener类:

http://msdn.microsoft.com/en-us/library/system.net.sockets.tcplistener.aspx

或者按照里德的建议去看看WCF。

答案 4 :(得分:0)

如果你想这样做,这里有一些建议:

  • 发送X个字节数不保证您将在客户端的单个Receive()中接收它们。你可以在客户端上有一个MemoryStream和一个缓冲区。使用缓冲区接收和写入MemoryStream,直到Receive返回0。
  • 完成数据发送后,请在调用Close()之前使用client.Shutdown(SocketShutdown.Send)(或.Both)。这将阻止客户端上的TCP RST。
  • 如果要序列化多个对象,只需在服务器中依次序列化它们。然后客户端将缓冲所有传入的数据,当Receive()返回0时,将客户端MemoryStream中的位置移动到0并逐个开始反序列化对象,直到ms.Position == ms.Length。