【问题标题】:How to send a binary stream from Java to C# via Tcp?如何通过 Tcp 将二进制流从 Java 发送到 C#?
【发布时间】:2019-09-23 15:06:59
【问题描述】:

我有一个c# server。我需要将 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);

【问题讨论】:

  • 你遇到了什么问题?
  • @ChetanRanpariya,服务器似乎没有收到 Java 客户端发送的字符串。服务器运行正常。
  • Java 的 DataStream 类以扩展的 UTF 编码字符串。

标签: java c# tcpclient tcplistener


【解决方案1】:

问题是您在 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 表示此方法首先将使用 BinaryWriter 实例的当前编码编码的字符串的长度(以字节为单位)写入流。该值写为无符号整数。然后,此方法将那么多字节写入流中。

例如,字符串“A”的长度为 1,但使用 UTF-16 编码时;长度是2个字节,所以写在prefix里面的值是2,写到stream里面有3个byte,包括prefix。

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 不工作。服务器和客户端都没有给出任何错误消息。
    • 做了一点研究,Java的DataOutputStream使用的是big endian,而C#的BinaryWriter使用的是little endian。您将不得不反转长度前缀之一。
    • 后一个代码如何与前一个代码相适应? strBytes 是什么意思?为什么ReadBytes 以 4 作为参数?
    • 我将假设后面的代码您指的是字节反转,它们替换了writer.Write(bytes.Length);int len = reader.ReadInt32(); 行。 strBytes 应该类似于存储我们的字符串的字节数组,以前称为bytesReadBytes 接受它应该读取的字节数作为参数,我们传入 4 因为一个 32 位整数占用 4 个字节 (32/8 = 4)。
    • @user366312 你能详细说明你所说的不工作是什么意思吗?我在 ideone 上尝试了一组类似的代码,效果很好:ideone.com/FB5MhT
    猜你喜欢
    • 2015-03-03
    • 2013-07-21
    • 1970-01-01
    • 2011-12-03
    • 1970-01-01
    • 2011-04-30
    • 2012-04-23
    • 1970-01-01
    • 2010-12-20
    相关资源
    最近更新 更多