【问题标题】:C# Sending multiple objects over same SocketC#通过同一个Socket发送多个对象
【发布时间】:2011-04-11 16:40:24
【问题描述】:

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

客户:

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) 处阻塞以等待另一个对象被发送。

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

【问题讨论】:

    标签: c# sockets serialization


    【解决方案1】:

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

    【讨论】:

    • 用 wcf 替换套接字在开销等方面会在极端之间跳跃
    • @Marc:确实,在某种程度上——如果开销在这种情况下非常重要,那么它可能是一个问题。尽管给定当前代码(打开/关闭连接并使用同步套接字代码执行所有操作),但感知到的开销很容易降低...
    • 我需要低开销的快速通信,否则 WCF 会很棒。大多数对象是 600-800 字节,但每隔一段时间就会有一个对象可以达到 3 MB。我需要以每秒 20-100 个的速度发送较小的。
    【解决方案2】:

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

    这将正确地挑选项目不会尝试过度阅读,直到流关闭,届时它将yield break。每个对象都会单独返回 (yield return),因此它不会在给您数据之前尝试消耗整个流。

    如果你愿意,它也可以用于异构数据,但同质是最简单的。

    【讨论】:

    • 嗯...这听起来很像我想要的。我会调查并回来的!谢谢:)
    • @Joe 在快速启动项目 IIRC 中有一个套接字示例
    • @Joe 记录一下,protobuf 的带宽和 CPU 几乎总是比 BinaryFormatter 小得多
    • 谢谢!我经历了一切,一切都很好,但我和我的教授谈过(这是一个学校项目),他说因为我没有写 protobuf,所以我不能使用它 :( 但是!我肯定会用这个在其他应用程序上。开销是如此之小!我将我的 800 字节序列化数组降至
    • @Joe 正确;现在...我想知道您的教授是否意识到您也没有编写 BinaryFormatter、Stream 等...课程是奇怪的环境,但这里要看的是“NIH”。在专业工作中,“NIH”是一种代价高昂的态度:)
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      查看 TcpListener 类:

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

      或者按照 Reed 的建议去研究 WCF。

      【讨论】:

        【解决方案5】:

        如果你想走这条路,这里有一些建议:

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

        【讨论】:

          猜你喜欢
          • 2010-12-28
          • 2012-06-19
          • 1970-01-01
          • 2013-07-07
          • 1970-01-01
          • 2015-11-17
          • 2014-11-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多