8

我有一个简单的信息:

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 侦听器,我有一个名为 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());
4

1 回答 1

6

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

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


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

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

于 2010-07-01T05:53:22.153 回答