Search code examples
javac#tcpclienttcplistener

How to send a binary stream from Java to C# via Tcp?


I have a c# server. I need to connect a Java client to it, and make it interact.

The following is a client-side C# code:

string Host = "localhost";
int Port = 2000;

TcpClient Tcp = new TcpClient(Host, Port);

NetworkStream stream = Tcp.GetStream();
reader = new BinaryReader(stream);
writer = new BinaryWriter(stream);

writer.Write("Hello");
string str = reader.ReadString();

What would be the Java equivalent of this code?

I have written the following:

    InetAddress ip = InetAddress.getByName("localhost"); 

    int PORT_NO = 2000; 
    Socket socket = new Socket(ip, PORT_NO); 

    // obtaining input and out streams 
    DataInputStream reader = new DataInputStream(socket.getInputStream()); 
    DataOutputStream writer = new DataOutputStream(socket.getOutputStream());

    writer.writeChars("Hello");
    String str = reader.readUTF();

But, my java code isn't working.

Server is running okay. The server seems to be not receiving the string sent by Java client.

How can I do what I need?

.

Edit: I used the following code in C# server according to @van dench's suggestion. Now, even C# clients stopped working.

write...

            byte[] strBytes = Encoding.UTF8.GetBytes(str);
            byte[] lenBytes = BitConverter.GetBytes(strBytes.Length);
            Array.Reverse(lenBytes);
            writer.Write(lenBytes);
            writer.Write(strBytes);
            writer.Flush(); 

read...

            byte[] lenBytes = reader.ReadBytes(4);
            Array.Reverse(lenBytes);
            int len = BitConverter.ToInt32(lenBytes, 0);
            byte[] bytes = reader.ReadBytes(len);
            string str = Encoding.UTF8.GetString(bytes);

Solution

  • Java's DataOutputStream and DataInputStream encode strings in a format known as Modified UTF-8. This basically means that a single character can be either 1, 2, or 3 bytes long. It is intended to write strings in a more compressed manor assuming that most people will be using ASCII characters. The leading bit in the encoded data is used to determine if there is another byte afterwards that is part of the same character.

    As best I can tell C#'s BinaryWriter and BinaryReader just encodes the raw UTF-16 data.

    The easiest solution is going be write a byte array instead of a string.

    In C# you're going to need the following:

    byte[] bytes = Encoding.UTF8.GetBytes(str);
    writer.Write(bytes.Length);
    writer.Write(bytes);  
    
    int len = reader.ReadInt32();
    byte[] bytes = reader.ReadBytes(len);
    string str = Encoding.UTF8.GetString(bytes);
    

    and in Java you will need:

    byte[] bytes = str.getBytes(StandardCharsets.UTF_8);
    writer.writeInt(bytes.length)
    writer.write(bytes, 0, bytes.length);  
    
    int len = reader.readInt();
    byte[] bytes = new byte[len];
    reader.read(bytes, 0, len);
    String str = new String(bytes, StandardCharsets.UTF_8);
    

    You can change the encoding to something else if you wish, but it has to be the same on the client and the server.

    Edit:

    Java prefers Big Endian while C# prefers Little Endian, because of this one of the lengths has to be reversed. Given that network byte order is big endian I would recommend doing this on the C# side.

    byte[] lenBytes = BitConverter.GetBytes(strBytes.Length);
    Array.Reverse(lenBytes);
    writer.Write(lenBytes);
    
    byte[] lenBytes = reader.ReadBytes(4);
    Array.Reverse(lenBytes);
    int len = BitConverter.ToInt32(lenBytes);