从C#客户端向Java服务器发送4字节消息头
我正在尝试将Cclient写入用Java编写的服务器。服务器需要一个4字节(Java中为DataInputStread readInt())的消息头,后跟实际的消息。
我绝对不熟悉C#,如何将这个消息头发送到Java Server?我尝试了几种方法(大多数情况下都是反复试验,而又不深入了解语言),但没有任何效果。 Java端的消息长度不正确(很大)。
解决方案
很简单,但是我们检查了字节序吗?我们发送数据的字节序与接收的字节序很容易不匹配。
我不知道C,但是我们只需要这样做:
out.write((len >>> 24) & 0xFF); out.write((len >>> 16) & 0xFF); out.write((len >>> 8) & 0xFF); out.write((len >>> 0) & 0xFF);
如果我们要交换大量数据,我建议我们实现(或者找到)可以按网络顺序写入和读取整数的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 }
正如其他张贴者所指出的那样,它是按字节顺序排列的。
Java DataInputStream期望数据为大端(网络字节顺序)。从Mono文档(对于像BinaryWriter这样的对等物)来看,Ctends倾向于小端(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应用程序以低字节序发送int引起的,而Java应用程序希望它们以网络顺序(大端序)进行发送。但是,与其在Capp中显式重新排列字节,不如正确的方法是依靠内置函数将主机顺序转换为网络顺序(Hton等)-这样,即使在Windows上运行,代码也可以继续正常工作大端机。
通常,在对此类问题进行故障排除时,我发现使用netcat或者wirehark之类的工具记录正确的流量(例如情况下的Java到Java),然后将其与错误的流量进行比较以查看出问题的地方很有用。另外,我们还可以使用netcat将捕获/预记录的请求注入服务器或者将捕获/预记录的响应注入客户端。更不用说我们还可以在开始修复代码之前,修改文件中的请求/响应并测试结果。
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)); }