Java 与 C++ 的 socket 通信问题

最近在由于某种原因,来了兴趣,准备做个项目,用 Spring Boot + Netty 写个简单的聊天服务器,其中有一个客户端用 MFC 来写。这里就涉及到 Java 与 C++ 的通信问题。

说这个之前,先来了解一下相关知识:字节序 详细介绍请看 wiki 的介绍《Endianness》(这个链接是英文的,中文的我这里打不开,不知道是不是被墙了)

字节序,即字节在电脑中存放时的序列与输入(输出)时的序列是先到的在前还是后到的在前。

主要有 big-endianlittle-endian 两种格式,

  • big-endian:内存地址从左到右与值由低到高的顺序相对应。
  • little-endian:地址低位存储值的低位 ,地址高位存储值的高位。

直观一点,直接上来自 wiki 的图
big-endian
little-endian

Java 遵循的是 big-endian,而 C++ 依赖于操作系统环境,大部分操作系统遵循的是 little-endian,Windows 下的是 little-endian
如果两者之间进行字节流通讯,比如 socket 传输,那么问题就来了!因为大家写内存的方式不同,单字节数据还没问题,但是多字节数据,就会导致乱码或者其他不正确的数据产生。举个例子,一个十进制数字 16909320,二进制表示即为 00000001 00000010 00000100 00001000 Java 和 C++ 的 int 类型占用 4 个字节,32bit,那么这个数字在内存显示的方式为

内存地址 A A+1 A+2 A+3
Java(Big-endian) 00000001 00000010 00000100 00001000
C++(Little-endian) 00001000 00000100 00000010 00000001

如果 Java 上数字是正确的,不做任何改变传输到 C++,然后会被解析成 00001000 00000100 00000010 00000001,即数字为 134480385 所以两者直接通信,必须对这方面做处理,无论是在传输前转换还是传输后再解析都可以。 处理的方法很多,其中一种比较简单就是移位,即把字节序调整正确:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class EndianUtil {
/**
* convert big-endian to little-endian
*/
public static final short BE2LE16(short x) {
return (short) ((x & 0xFF) << 8 | 0xFF & (x >> 8));
}

/**
* convert big-edian to little-edian
*/
public static final int BE2LE32(int x) {
return (x & 0xFF) << 24 | (0xFF & x >> 8) << 16 | (0xFF & x >> 16) << 8 | (0xFF & x >> 24);
}
}