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

时间:2019-05-06 00:01:43

标签: java c# tcpclient tcplistener

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

以下是客户端C#代码:

ids = ['rex', 'test']
pwds = ['rex', 'test']

def user():
    adding_username = raw_input('username: ')
    adding_password = raw_input('password: ')
    ids.append(adding_username)
    pwds.append(adding_password)

    # print the ids list
    print(ids)

    # print the pwds list
    print(pwds)

    # write the lists to a file "myfile.txt"
    with open("myfile.txt", "w") as myfile:
        # use the 'str' function on an object to turn it to string
        # or in other words turn it to "text"
        myfile.write(str(ids))
        myfile.write("\n") # start new line in file
        myfile.write(str(pwds))

    print('\nadded: {}:{}'.format(adding_username, adding_password))

user()

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

我写了以下内容:

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代码无法正常工作。

服务器运行正常。服务器似乎未收到Java客户端发送的字符串。

我该怎么做?

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

写...

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

阅读...

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

2 个答案:

答案 0 :(得分:3)

Java的DataOutputStreamDataInputStream以称为“已修改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);

答案 1 :(得分: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_

  

长度前缀表示使用BinaryWriter实例的当前编码进行编码时,此方法首先将字符串的长度(以字节为单位)写入流。此值写为无符号整数。然后,此方法将那么多字节写入流。

     

例如,字符串“ A”的长度为1,但使用UTF-16编码时;长度为2个字节,因此写在前缀中的值为2,并将3个字节(包括前缀)写入流中。

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