【发布时间】:2010-09-10 16:11:12
【问题描述】:
我正在尝试将 C# 客户端写入用 Java 编写的服务器。服务器需要一个 4 字节(Java 中的 DataInputStread readInt())消息头,后跟实际消息。
我对 C# 完全陌生,如何将此消息头发送到 Java 服务器?我尝试了几种方法(主要是在没有深入了解 C# 语言的情况下反复试验),但没有任何效果。 Java 端的消息长度不正确(非常大)。
【问题讨论】:
我正在尝试将 C# 客户端写入用 Java 编写的服务器。服务器需要一个 4 字节(Java 中的 DataInputStread readInt())消息头,后跟实际消息。
我对 C# 完全陌生,如何将此消息头发送到 Java 服务器?我尝试了几种方法(主要是在没有深入了解 C# 语言的情况下反复试验),但没有任何效果。 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 期望数据为big-endian(网络字节顺序)。从 Mono 文档(对于像 BinaryWriter 这样的等价物)来看,C# 倾向于小端(Win32/x86 的默认设置)。
所以,当您使用标准类库将 32bit 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 应用程序期望它们以网络顺序(大端)。但是,正确的方法不是在 C# 应用程序中显式地重新排列字节,而是依靠内置函数从主机转换为网络顺序(htons 等)——这样即使在运行时,您的代码也将继续正常工作在大端机器上。
一般来说,在解决此类问题时,我发现使用 netcat 或 wireshark 等工具记录正确的流量(例如,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));
}
【讨论】: