从C#客户端向Java服务器发送4字节消息头

我正在尝试将C#客户端编写到用Java编写的服务器上。 服务器需要一个4字节(Java中的DataInputStread readInt())消息头,后跟实际消息。

我是C#的新手,如何将此消息头发送到Java Server? 我尝试了几种方法(主要是试验和错误,而没有深入研究C#语言),没有任何效果。 Java端最终得到了不正确的(非常大的)消息长度。

正如其他海报所指出的那样,它一直是字节序。

Java DataInputStream期望数据是big-endian (网络字节顺序)。 从Mono文档(对于像BinaryWriter这样的等价物)来看,C#倾向于小端(Win32 / x86的默认值)。

因此,当您使用标准类库将32位int’1’更改为字节时,它们会产生不同的结果:

//byte hex values Java: 00 00 00 01 C#: 01 00 00 00 

您可以改变在C#中编写int的方式:

 private static void WriteInt(Stream stream, int n) { for(int i=3; i>=0; i--) { int shift = i * 8; //bits to shift byte b = (byte) (n >> shift); stream.WriteByte(b); } } 

编辑:

更安全的方法是:

 private static void WriteToNetwork(System.IO.BinaryWriter stream, int n) { n = System.Net.IPAddress.HostToNetworkOrder(n); stream.Write(n); } 

这很简单,但你检查了字节顺序吗? 您发送数据的字节顺序与您收到的字节顺序之间很容易不匹配。

正如这里的每个人都已经指出的那样,问题很可能是由于C#应用程序以小端顺序发送整数,而Java应用程序期望它们按网络顺序(big-endian)。 但是,不是在C#app中明确重新排列字节,而是正确的方法是依靠内置函数将主机转换为网络顺序(htons等) – 这样你的代码即使在运行时也能继续正常工作在一台大端机器上。

通常,在对这些问题进行故障排除时,我发现使用netcat或wireshark等工具记录正确的流量(例如,Java中的Java)非常有用,然后将其与不正确的流量进行比较,以查看它出错的地方。 作为额外的好处,您还可以使用netcat将捕获/预先录制的请求注入服务器,或将捕获/预先录制的响应注入客户端。 更不用说您还可以修改文件中的请求/响应,并在开始修复代码之前测试结果。

如果您要交换大量数据,我建议实现(或查找)可以按网络顺序写入和读取整数的Stream-wrapper。 但如果你真的只需要写长度做这样的事情:

 using(Socket socket = ...){ NetworkStream ns = new NetworkStream(socket); ns.WriteByte((size>>24) & 0xFF); ns.WriteByte((size>>16) & 0xFF); ns.WriteByte((size>>8) & 0xFF); ns.WriteByte( size & 0xFF); // write the actual message } 

我不知道C#,但你只需要做相同的事情:

 out.write((len >>> 24) & 0xFF); out.write((len >>> 16) & 0xFF); out.write((len >>> 8) & 0xFF); out.write((len >>> 0) & 0xFF); 

Sysetm.Net.IPAddress类有两个静态辅助方法:HostToNetworkOrder()和NetworkToHostOrder()为您进行转换。 您可以在流上使用BinaryWriter来写入正确的值:

 using (Socket socket = new Socket()) using (NetworkStream stream = new NetworkStream(socket)) using (BinaryWriter writer = new BinaryWriter(stream)) { int myValue = 42; writer.Write(IPAddress.HostToNetworkOrder(myValue)); }