Чтение сообщения Protobuf в C # - PullRequest
5 голосов
/ 01 июля 2010

У меня есть простое сообщение:

пакетный тест;

message sendName {

необязательная строка username = 1;

}

Удивительный плагин VS генерирует файл .cs:

тест пространства имен {

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

открытый частичный класс 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());

1 Ответ

5 голосов
/ 01 июля 2010

Serializer.Deserialize<sendName>(stream); должен это сделать, но я догадываюсь , что он висит навсегда? Причина в том, что сообщения protobuf не имеют встроенного терминатора, поэтому по умолчанию заключается в том, что оно читает до конца потока - что не произойдет, пока вы не закроете сокет.

Чтобы обойти это, обычным трюком является префикс сообщения с длиной, и ограничьте себя этим. Там может быть встроенный способ справиться с этим в реализации Java, или вы можете справиться с этим вручную. На стороне C # protobuf-net имеет DeserializeWithLengthPrefix, который принимает перечисление, чтобы указать, как вы его закодировали. Для простоты я бы предложил базовое 32-разрядное кодирование с прямым порядком байтов длины (которое отображается на PrefixStyle.Fixed32).


Отредактируйте / прокомментируйте: оба должны совпадать. В данный момент вы сериализуете без префикса длины, но десериализуете , ожидая префикса длины. Предоставляет ли API Java механизм для получения длины данных до того, как вы ее напишите? Если нет, возможно, сначала запишите во временный буфер, посмотрите, сколько вы написали, затем запишите длину и, наконец, данные.

Или; если вы отправляете только одно сообщение - просто закройте поток и используйте Deserialize (без префикса длины). У меня есть пример сокетов для нескольких сообщений C # -to-C #, но я не могу много комментировать на стороне Java ...

...