在 C# 中读取 Protobuf 消息

发布于 2024-09-07 08:35:03 字数 3218 浏览 5 评论 0 原文

我有一个简单的消息:

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# 应用程序中,它的作用类似于 Socket Listener,我有一个方法称为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());

I have a simple message:

package test;

message sendName {  
  optional string username = 1; 
}

The awesome VS plugin generates the .cs file:

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);
        }
    }
}

I am sending a message from the java side by using

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

In my C# application, which acts like the Socket Listener, I have a method called Listen which listens for the message sent by the java client:

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);
    }
}

I am obviously missing some basics here.

What method should I use to get the sendName object?


When I debug my code in C#, the debugger quits when I call DeserializeWithLengthPrefix method.

This is my C# code:

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);
        }
    }
}

This is my java code:

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

纵性 2024-09-14 08:35:03

Serializer.Deserialize(stream); 应该可以做到这一点,但我猜测它会永远挂起?这里的原因是 protobuf 消息没有内置终止符,因此默认情况下它会读取直到流结束 - 这在您关闭套接字之前不会发生。

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


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

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

The Serializer.Deserialize<sendName>(stream); should do it, but I'm guessing that it is hanging forever? The reason here is that protobuf messages don't have an inbuilt terminator, so the default is that it reads until the end of the stream - which won't happen until you close the socket.

To get around this, a common trick is to prefix the message with the length, and limit yourself to that. There may be an inbuilt way to handle that in the Java implementation, or you can handle it manually. On the C# side, protobuf-net has DeserializeWithLengthPrefix, which accepts an enum to specify how you've encoded it. For simplicity I would suggest a basic 32-bit little-endian encoding of the length (which maps to PrefixStyle.Fixed32).


Re the edit / comment: the two must match. At the moment you are serializing without a length prefix, but deserializing expecting a length prefix. Does the Java API expose a mechanism to obtain the data-length before you write it? If not, perhaps write first to a temp buffer, see how much you wrote, then write the length and finally the data.

Or; if you are only sending a single message - just close the stream and use Deserialize (no length prefix). I have a C#-to-C# multi-message sockets example available, but I can't comment much on the java side...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文