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

Socket Listener のように動作する私の C# アプリケーションには、Java クライアントから送信されたメッセージをリッスンする Listen というメソッドがあります。

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、enum を受け入れてエンコード方法を指定する があります。簡単にするために、長さの基本的な 32 ビット リトル エンディアン エンコーディングをお勧めします (これは にマップされますPrefixStyle.Fixed32)。


編集/コメントについて: 2 つが一致する必要があります。現時点では、長さのプレフィックスなしでシリアル化していますが、長さのプレフィックスを期待して逆シリアル化しています。Java API は、書き込む前にデータ長を取得するメカニズムを公開していますか? そうでない場合は、おそらく最初に一時バッファーに書き込み、書き込み量を確認してから、長さを書き込み、最後にデータを書き込みます。

または; 単一のメッセージのみを送信する場合は、ストリームを閉じて使用しますDeserialize(長さのプレフィックスなし)。C# から C# へのマルチメッセージ ソケットの例を利用できますが、Java 側についてはあまりコメントできません...

于 2010-07-01T05:53:22.153 に答える