本本人从事Android开发工作,最近由于工作需要,需要与C++语言服务器进行TCP交互。在java中Socket编程已经为我们封装了很多好用的Api,今天我们讨论的是和C++交互过程中的一些心得。首先要明确几个概念 :
什么是内存对齐为什么要内存对齐-内存对齐:
内存对齐,或者说字节对齐,是一个数据类型所能存放的内存地址的属性(Alignment is a PRoperty of a memory address)。 这个属性是一个无符号整数,并且这个整数必须是2的N次方(1、2、4、8、……、1024、……)。 当我们说,一个数据类型的内存对齐为8时,意思就是指这个数据类型所定义出来的所有变量,其内存地址都是8的倍数。
当一个基本数据类型(fundamental types)的对齐属性,和这个数据类型的大小相等时,这种对齐方式称作自然对齐(naturally aligned)。 比如,一个4字节大小的int型数据,默认情况下它的字节对齐也是4。 -为什么要内存对齐:
这是因为,并不是每一个硬件平台都能够随便访问任意位置的内存的。我个人的理解可能在不同的平台上面内存的分配情况是不一样的。这个也是导致java中没有结构体这个数据结构的原因。 我们都知道,在结构体内部可能有多个基本类型,那么子阿布同平台他所占的内存大小也是不一样的的使用。那么问题来了,java中如何实现C或C++中的结构体类型。
-Javolution 这是一个第三方资源库。 [ Javolution ]
javolution的使用:
1 javolution支持maven方式引入
<dependency> <groupId>org.javolution</groupId> <artifactId>javolution-core-java</artifactId> <version>6.0.0</version> </dependency>2 可以下载相应的Jar加入到你的工程中。(注意:我开始下载的是5.X版本,有些问题。下载6.x就好了)。
开始使用Javolution: 在java中若要定义结构体形式的数据结构。你的类首先应该继承Javolution的Struct类。而且java中的基本类型也不能直接使用。要使用Struct类中的基本类型,如下:
public final Signed32 version = new Signed32(); public final Signed32 eMainType = new Signed32(); public final Bool hasSubtype = new Bool(); public final Signed32 nLen = new Signed32(); public final UTF8String HeadEnding = new UTF8String(2); public final Signed32 eSubType = new Signed32();值得注意的是:在你自己定义的“结构体”中,ByteOrder方法返回的小端对齐的。
另外在附加两个工具方法。分别是Struct转成byte[]和byte[]转成Struct:
/** * 把类型转化成byte数组 * @param struct 实体类型 * @return */ public static byte[] GetBytesFromStruct( javolution.io.Struct struct) { int sz = struct.size(); ByteBuffer bb = struct.getByteBuffer(); if( bb == null)return null; byte[] buffer = new byte[sz]; bb.position(0); // 设置位置 bb.get(buffer);// System.out.println(bb); return buffer; }/** * 把byte数组转换成类型 * * @param struct 实体类型 * @param bytes 原始数据流 * @param offset 开始位置 * @param length 取长度 * @return */ public static Struct GetStructFromByte(Struct struct, byte[] bytes,int offset,int length) { ByteBuffer bb = ByteBuffer.wrap(bytes, offset, length); bb.position(0); bb.order(ByteOrder.LITTLE_ENDIAN); struct.setByteBuffer(bb, 0); return struct; } 值得注意的是: 1 关于如何为结构体赋值或是获取其中某个属性的值可以通过Struct.XXX.set()和 Struct.XXX.get()方法实现。 2 在你自己定义的结构体中,每个参数的顺序要按照协议约定的顺序,否则返回的结构将不是你期待的结果。 3 获取你自己定义的结构的内存大小使用的Struct.Size()方法。网上有对这种方式的问题做出了总结,目前本人还没有遇到: 一 对齐方式改变了:结构体在遇到64位的数据类型的时候,就自动把前面的数据按8字节对齐了,这样子的话coin数据本应该是由数据流的第12字节的位置开始解析变成了第16个字节开始解析,后面的数据就必然会跟着移位 二 在C中char content[0]content数据存储的是可变数据的开始地址,在c的结构体里是不占用大小的,所以这个结构体的大小应该为2.在java里就定义不出来的
详细问题与解决方式的请参考。 [ Javolution的坑 ]
以上就是在这次工作中的一点心得,另外网上还有一种实现方式是通过JNI来实现的。个人还没有尝试。
新闻热点
疑难解答