命名管道:读取速度太慢时数据不正确?

时间:2014-04-15 01:58:20

标签: c# winapi named-pipes

我使用NamedPipeServerStream / NamedPipeClientStream构建了一个C#应用程序,当客户端读取速度太慢时,我无法序列化对象。这是我可以放在一起的最小例子。

客户(纯消费者):

NamedPipeClientStream pipeClient;
if (useString)
    reader = new StreamReader(pipeClient);
while (true)
{
    Thread.Sleep(5000);
    if (useString)
    {
        string line = reader.ReadLine();
    }
    else
    {
        Event evt = (Event)formatter.Deserialize(pipeClient);
    }
}

(纯生产者):

while (true)
{
    i++;
    Thread.Sleep(1000);
    if (useStrings)
    {
        StreamWriter writer = new StreamWriter(m_pipeServer);
        writer.WriteLine("START data payload {0} END", i);
        writer.Flush();
    }

    else
    {
        BinaryFormatter formatter = new BinaryFormatter();
        formatter.Serialize(m_pipeServer, new Event(i));
    }

    m_pipeServer.Flush();
    m_pipeServer.WaitForPipeDrain();
}

“Event”是一个简单的类,其中有一个属性跟踪有效载荷:i。

当服务器为客户端生成太多而无法读取时,我期望的行为只是“丢失”事件。但是,在字符串的情况下,我得到一个随机的事件排序:

START data payload 0 END
START data payload 1 END
START data payload 2 END
START data payload 4 END
START data payload 15 END
START data payload 16 END
START data payload 24 END
START data payload 3 END
START data payload 35 END
START data payload 34 END
START data payload 17 END

对于二进制序列化器,我得到一个例外(这不太令人惊讶):

SerializationException:二进制流“0”不包含有效的BinaryHeader。可能的原因是序列化和反序列化之间的无效流或对象版本更改。

最后,请注意,如果我在客户端上删除对Sleep的调用,一切正常:按顺序接收所有事件(按预期)。

所以我试图弄清楚当客户端读取太慢而错过事件时如何在命名管道上序列化二进制事件。在我的场景中,丢失的事件是完全正常的。但是,我很惊讶链接完整的字符串事件而不是截断(由于缓冲区翻转)或简单地丢弃。

二进制格式化程序案例实际上是我关心的。我正在尝试序列化并将命名管道中相对较小的事件(约300字节)传递给多个消费者程序,但我担心这些客户端无法跟上卷。

如果我们耗尽缓冲区,如何在命名管道中正确生成/使用这些事件?我希望的行为只是丢弃客户端无法跟上的事件。

1 个答案:

答案 0 :(得分:3)

我不相信传输层(即管道)丢弃客户端无法跟上的数据包。我会在客户端上创建一个循环队列。然后,专用线程将为管道提供服务并将消息放入队列。单独的客户端线程(或多个线程)将为队列提供服务。这样做,你应该能够保持管道清洁。

由于它是一个循环队列,较新的消息将覆盖旧的消息。客户端读取将始终获取尚未处理的最旧消息。

创建循环队列非常简单,您甚至可以使其实现IProducerConsumerCollection,以便您可以将其用作BlockingCollection的后备存储。