4

我正在使用 Jon Skeet 的(优秀的)谷歌协议缓冲区到 C#/.Net 的端口。

为了练习,我编写了一个虚拟即时通讯应用程序,它通过套接字发送一些消息。我有一个消息定义如下: -

message InstantMessage {<br/>
  required string Message = 1;<br/>
  required int64 TimeStampTicks = 2; <br/>
}

当发送者序列化消息时,它会非常优雅地发送它:-

        ...
        InstantMessage.Builder imBuild = new InstantMessage.Builder();

        imBuild.Message = txtEnterText.Text;
        imBuild.TimeStampTicks = DateTime.Now.Ticks;

        InstantMessage im = imBuild.BuildPartial();

        im.WriteTo(networkStream);
        ...

这很好用。但在另一端,我无法ParseFrom正常工作。

我想使用:-

InstantMessage im = InstantMessage.ParseFrom(networkStream);

但相反,我不得不将其读取为字节,然后从这里解析它。由于多种原因,这显然不是理想的。当前代码是:-

while (true)
        {
            Byte[] byteArray = new Byte[10000000];

            int intMsgLength;
            int runningMsgLength = 0;

            DateTime start = DateTime.Now;

            while (true)
            {
                runningMsgLength += networkStream.Read(byteArray, runningMsgLength, 10000000 - runningMsgLength);

                if (!networkStream.DataAvailable)
                    break;

            }

            InstantMessage im = InstantMessage.ParseFrom(byteArray.Take(runningMsgLength).ToArray());

当我尝试使用ParseFrom时,即使我知道在线上有有效的 GB 消息,控制也不会返回到调用方法。

任何建议将不胜感激,

私服

4

2 回答 2

4

很抱歉花了一些时间来回答这个问题。正如 Marc 所说,协议缓冲区没有终止符,除非它们是嵌套的,否则它们没有长度前缀。但是,您可以自己添加长度前缀。如果您查看 MessageStreamIterator 和 MessageStreamWriter,您会看到我是如何做到这一点的 - 基本上我假装自己在消息中间,将嵌套消息写为字段 1。不幸的是,在阅读消息时,我必须使用 internal详细信息(BuildImpl)。

现在有另一个 API 可以做到这一点:IMessage.WriteDelimitedToIBuilder.MergeDelimitedFrom. 这可能是您目前想要的,但我似乎记得在检测流结束方面存在一个小问题(即当没有其他消息要读取时)。我不记得目前是否有修复它 - 我感觉它在 Java 版本中发生了变化,我可能还没有移植这个变化。无论如何,这绝对是值得关注的领域。

于 2010-08-30T08:41:41.377 回答
0

Protobuf 没有终结符 - 所以要么关闭流,要么使用您自己的长度前缀等。Protobuf-net 通过 SerializeWithLenghtPrefix / DeserializeWithLengthPrefix 轻松公开这一点。

简单地说:没有这个,它就无法知道每条消息在哪里结束,所以一直试图读到流的末尾。

于 2010-08-27T15:58:32.870 回答