views:

94

answers:

1

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());
+2  A: 

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...

Marc Gravell
Marc, I keep getting a "Invalid Token :0" exception. Can you tell me what this might mean?
sonu
@sonu - that means it is finding a 0 at the start a bunch of data, which is *never* valid. Can you add any detail around what the data-stream looks like and what the code looks like when you are seeing this?
Marc Gravell
Sorry I'm new to stackoverflow. I added the code by editing the post. When I close the socket on the java side, I get an exception : "Attempted to read past the end of stream"
sonu
Marc, In java there is writeDelimitedTo which is equivalent to DeserializeWithLengthPrefix. I think that will do it. Thanks.
sonu