我正在尝试编写一个C#客户端与Java编写的服务器通信。该服务器期望收到一个4字节(Java中的DataInputStread readInt())的消息头,然后是实际的消息内容。
我对C#完全不熟悉,如何将此消息头发送到Java服务器?我尝试了几种方式(大多数是尝试和错误,没有深入研究C#语言),但都没有成功。Java端最终收到了不正确的(非常大的)消息长度。
正如其他帖子所指出的那样,这取决于字节序。
Java DataInputStream 期望数据为big-endian(网络字节顺序)。从Mono文档中可以看出(例如BinaryWriter的等效类),C# 倾向于采用小端序(Win32/x86 的默认值)。
因此,当您使用标准类库将32位整数“1”转换为字节时,它们会产生不同的结果:
//byte hex values
Java: 00 00 00 01
C#: 01 00 00 00
你可以改变在C#中编写整数的方式:
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);
}
这很简单,但你是否检查了字节序?发送数据时使用的字节序与接收数据时使用的字节序可能不匹配。
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);
System.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));
}