我有一个简单的信息:
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/