2

I'm not exactly sure what the issue is here, but it seems like all of the data that I'm sending to Java is being corrupted. It works completely fine with a Java client and I've validated the Byte sizes of every primitive between the two languages.

I'll start with the C# aspect of the client, as I've made it very simple:

This is the PacketBuilder class.

/// <summary>
    /// Used to build a instance of the <see cref="UnityNetworking.Packet"/> class.
    /// </summary>
    public class PacketBuilder {

        /// <summary>
        /// The opcode of the packet being built.
        /// </summary>
        private int Opcode;

        /// <summary>
        /// The stream to write data to the packet's buffer.
        /// </summary>
        private MemoryStream stream;

        /// <summary>
        /// The binary writer to convert data into binary format.
        /// </summary>
        private BinaryWriter writer;

        /// <summary>
        /// The String encoder.
        /// </summary>
        private Encoding encoder;

        /// <summary>
        /// Create a new PacketBuilder instance with the specified opcode and a default capacity.
        /// </summary>
        /// <param name="opcode">The opcode.</param>
        public static PacketBuilder Create(int opcode) {
            return new PacketBuilder (opcode, 512);
        }

        /// <summary>
        /// Create a new PacketBuilder instance with the specified opcode and capacity.
        /// </summary>
        /// <param name="opcode">The opcode.</param>
        /// <param name="capacity">The buffer capacity.</param>
        public static PacketBuilder Create(int opcode, int capacity) {
            return new PacketBuilder (opcode, capacity);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="UnityNetworking.PacketBuilder"/> class.
        /// </summary>
        /// <remarks>Private scope to prevent outside library instantiation.</remarks>
        private PacketBuilder (int opcode, int capactiy) {
            Opcode = opcode;
            stream = new MemoryStream (capactiy);
            writer = new BinaryWriter (stream);
            encoder = new UTF8Encoding (true, true);
        }

        /// <summary>
        /// Adds the specified data to the builders buffer.
        /// </summary>
        /// <param name="data">The data.</param>
        public PacketBuilder Add(object data) {
            if (data is Int16) {
                writer.Write ((short)data);
            } else if (data is Int32) {
                writer.Write ((int)data);
            } else if (data is Int64) {
                writer.Write ((long)data);
            } else if (data is Single) {
                writer.Write ((float)data);
            } else if (data is Double) {
                writer.Write ((double)data);
            } else if (data is Byte) {
                writer.Write ((byte)data);
            } else if (data is Boolean) {
                writer.Write ((bool)data);
            } else if (data is String) {
                string str = (string)data;
                byte[] bytes = encoder.GetBytes (str);
                writer.Write ((short)bytes.Length);
                writer.Write (bytes, 0, bytes.Length);
            } else {
                throw new Exception ("Unsupported Object Type: " + data);
            }
            Debug.Log ("Data Type: " + data.GetType() + " || Value: " + data);
            return this;
        }

        public Packet ToPacket() {
            return new Packet(Opcode, stream.ToArray());
        }
    }

As you can see this class makes use of the MemoryStream and BinaryWriter for the example that I'm going to send I'm going to be using the long value or more commonly referred to as Int64 in C#.

    PacketBuilder builder = PacketBuilder.Create(1);
    builder.Add((byte)1);
    builder.Add(1337L);
    client.Write(builder.ToPacket());

This is send through the Write(Packet) method in the Client class, found here:

public NetworkClient Write(Packet packet) {
    networkStream.Write (packet.Buffer, 0, packet.Size);
    return this;
}

Very basic: Now on the Java end everything is being put into a ByteBuffer which data is taken from using getX methods.

The way that I would go about getting this data is the following:

public boolean handle(long userId, Session session, Packet packet) {
    ByteBuffer buffer = packet.getBuffer();
    byte opcode = buffer.get();
    userId = buffer.getLong();
    System.out.println("Opcode: " + opcode + " || User ID: " + userId);
}

This outputs the following line:

Opcode: 1 || User ID: 4108690235045445632

I don't understand what's going on at all, especially not how 1337 can turn into god only knows what that number is. When debugging from the client, the following information is presented:

Data Type: System.Byte || Value: 1
Data Type: System.Int64 || Value: 1337

So, if anyone can enlighten me as to WHAT IS GOING ON, that'd be nice.

Hobbyist
  • 15,888
  • 9
  • 46
  • 98

2 Answers2

1

You have different byte orders for your 64bit integer.

The value you are getting is 1337L which is 0x0539. If you reverse this byte value you get 0x3905000000000000. Convert this to decimal and you get 4108690235045445632.

Usually, when sending raw looking data over the network (like packets) you will use something like lton(value) to send, and ntol(value) on values you receive. I'm not sure what the C# and Java equivalent would be, but these are "long to network" and "network to long" functions. Similarly, you would do this for short and other types (other than 8 bit values).

Les
  • 10,335
  • 4
  • 40
  • 60
  • 1
    This could be an [Endianness](http://en.wikipedia.org/wiki/Endianness) issue, depending on the machines. I know Java is big endian and I think Intel machines are little endian. So if your C# program is running on an Intel based machine, that could be your issue. – Icemanind May 29 '15 at 22:36
  • Java uses big endian in the specification and `DataOutputStream` use it. You have to work extra to use little endian in Java. C# does not define an endianess but the `BinaryWriter` uses little endian. So you need to work extra to use big endian in C#. – m4ktub May 29 '15 at 22:47
  • @Icemandind - you may have intended comment for Op. yes, clearly an Endianness issue. – Les May 29 '15 at 22:49
  • @Les - Thanks for the clarification on reversing the byte values, I took care of this and now all of my networking is working perfectly from Client->Server. Now I just have to reverse the process from Server->Client. Thanks again. – Hobbyist May 29 '15 at 22:59
0

The BinaryWriter uses the Little Endian encoding while Java uses Big Endian. This means that the least significant byte of the Int64 is written first in C# and Java interprets that has the most significant byte.

Check here for a way to force Big Endian on C# side: BinaryWriter Endian issue

Community
  • 1
  • 1
m4ktub
  • 3,061
  • 1
  • 13
  • 17