c# - 在 C# 中读取 Protobuf 消息

标签 c# protobuf-net

我有一个简单的信息:

package test;

message sendName {  
  optional string username = 1; 
}

很棒的 VS 插件生成 .cs 文件:

namespace test {  

    [global::System.Serializable global::ProtoBuf.ProtoContract(Name=@"sendName")]

    public partial class sendName : global::ProtoBuf.IExtensible {

        public sendName() {}    

        private string _username = "";
        [global::ProtoBuf.ProtoMember(1, IsRequired = false, Name=@"username", DataFormat = > global::ProtoBuf.DataFormat.Default)]
        [global::System.ComponentModel.DefaultValue("")]
        public string username
        {
            get { return _username; }
            set { _username = value; }
        }

        private global::ProtoBuf.IExtension extensionObject;

        global::ProtoBuf.IExtension global::ProtoBuf.IExtensible.GetExtensionObject(bool createIfMissing)
        {
            return global::ProtoBuf.Extensible.GetExtensionObject(ref extensionObject, createIfMissing);
        }
    }
}

我正在使用 java 端发送消息

objName.build().writeTo((FileOutputStream)socket.getOutputStream());

在我的 C# 应用程序中,它的作用类似于套接字监听器,我有一个名为 Listen 的方法,用于监听 java 客户端发送的消息:

public void Listen()
{

    IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
    TcpListener listener = new TcpListener(ipAddress, 4055);
    TcpClient client = null;

    listener.Start();

    while (true)
    {
        Debug.WriteLine("Waiting for a Connection");

        client = listener.AcceptTcpClient();

        Stream stream = client.GetStream();

        // sendName sendNameObj = Serializer.Deserialize<sendName>(stream);
    }
}

我显然在这里缺少一些基础知识。

我应该使用什么方法来获取 sendName 对象?


当我在 C# 中调试我的代码时,调试器会在我调用 DeserializeWithLengthPrefix 方法时退出。

这是我的 C# 代码:

private void Listen()
{
    IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
    TcpListener listener = new TcpListener(ipAddress,4055);
    listener.Start();
    listener.BeginAcceptSocket(ClientConnected, listener);
}

private void ClientConnected(IAsyncResult result)
{
    TcpListener server = (TcpListener)result.AsyncState;
    using (TcpClient client = server.EndAcceptTcpClient(result))
    using (NetworkStream stream = client.GetStream())
    {
        try
        {
            //SendNameMessage sendNameObj = Serializer.Deserialize<SendNameMessage>(stream);
            SendNameMessage sendNameObj = Serializer.DeserializeWithLengthPrefix<SendNameMessage>(stream, PrefixStyle.Fixed32);
            string name = sendNameObj.sendName;
            if (name != null && name.Length > 0)
            {
                nameTextBox.Text = name;
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }
}

这是我的java代码:

SendNameMessage.Builder sendNameMessageObj = null;
sendNameMessageObj = SendNameMessage.newBuilder();
sendNameMessageObj.setSendName("Protobuf-net");

SocketRequest.INSTANCE.openSocket();
sendNameMessageObj.build().writTo(SocketRequest.INSTANCE.getWriter());

最佳答案

Serializer.Deserialize<sendName>(stream);应该这样做,但我猜测它会永远挂起?这里的原因是 protobuf 消息没有内置的终止符,所以 默认 是它读取到流的末尾 - 在您关闭套接字之前不会发生这种情况。

为了解决这个问题,一个常见的技巧是在消息的前缀长度上限制自己。在 Java 实现中可能有一种内置的方法来处理它,或者您可以手动处理它。在 C# 端,protobuf-net 有 DeserializeWithLengthPrefix ,它接受一个枚举来指定你如何编码它。为简单起见,我建议对长度进行基本的 32 位小端编码(映射到 PrefixStyle.Fixed32 )。


重新编辑/评论:两者必须匹配。目前您正在序列化没有长度前缀,但反序列化期望有长度前缀。 Java API 是否提供一种机制来在您编写它之前 获取数据长度?如果没有,也许先写入一个临时缓冲区,看看你写入了多少,然后写入长度,最后写入数据。

或者;如果您只发送一条消息 - 只需关闭流并使用 Deserialize (没有长度前缀)。我有一个 C#-to-C# 多消息套接字示例可用,但我不能在 Java 方面发表太多评论...

关于c# - 在 C# 中读取 Protobuf 消息,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3153719/

相关文章:

java - 我有哪些选项可以读取 Excel 文件并评估 C#、Java 和 PHP 中的公式?

c# - 这个 'double +=' 线程安全吗?

c# - Azure Function Blob 触发器将文件复制到文件共享

c# - 部分文本搜索的快速算法

protobuf-net - 了解正在发送的 protobuf 消息类型的 API

c# - Protobuf-net 对象引用反序列化使用字典 : A reference-tracked object changed reference during deserialization

c# - 使用消息泵异步检查 Azure 队列

c# - 如何在 proto 文件导入语句中使用相对路径?

c# - RuntimeTypeModel.Add方法中的 "applyDefaultBehaviour"有什么作用?

c# - 如何使用 protobuf-net 处理 .proto 文件