如何通过 Tcp 将二进制流从 Java 发送到 C#?

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

我有一个。我需要将 Java 客户端连接到它,并使其交互。

以下为客户端C#代码:

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

此代码的 Java 等价物是什么?

我写了以下内容:

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

但是,我的 java 代码不起作用。

服务器 运行 没问题。服务器似乎没有收到 Java 客户端发送的字符串。

我怎样才能做我需要的事情?

编辑: 我根据@van dench的建议在C#服务器中使用了以下代码。现在,连 C# 客户端都停止工作了。

写...

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

阅读...

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

问题是您在 C# 代码中使用了 ReadString 和 Write 方法。他们使用 Java 不知道的长度前缀格式。

https://docs.microsoft.com/en-us/dotnet/api/system.io.binarywriter.write?redirectedfrom=MSDN&view=netframework-4.8#System_IO_BinaryWriter_Write_System_String_

Length-prefixed means that this method first writes the length of the string, in bytes, when encoded with the BinaryWriter instance's current encoding to the stream. This value is written as an unsigned integer. This method then writes that many bytes to the stream.

For example, the string "A" has a length of 1, but when encoded with UTF-16; the length is 2 bytes, so the value written in the prefix is 2, and 3 bytes are written to the stream, including the prefix.

https://docs.microsoft.com/en-us/dotnet/api/system.io.binaryreader.readstring?view=netframework-4.8

Java 的 DataOutputStreamDataInputStream 以一种称为 Modified UTF-8 的格式对字符串进行编码。这基本上意味着单个字符可以是 1、2 或 3 个字节长。假设大多数人将使用 ASCII 字符,它旨在以更压缩的方式编写字符串。编码数据中的前导位用于确定后面是否有另一个字节是同一字符的一部分。

据我所知,C# 的 BinaryWriterBinaryReader 只是对原始 UTF-16 数据进行编码。

最简单的解决方案是编写字节数组而不是字符串。

在 C# 中,您将需要以下内容:

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

在 Java 中,您将需要:

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

您可以根据需要将编码更改为其他编码,但客户端和服务器上的编码必须相同。

编辑:

Java 更喜欢 Big Endian 而 C# 更喜欢 Little Endian,因为其中一个长度必须颠倒。鉴于网络字节顺序是大端,我建议在 C# 端执行此操作。

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