1 概述
(1)項目開發過程可能涉及多種語言,而多種語言之間如何數據交換格式是多種多樣的,比如說:Java和JavaScript可以用json,Java和C#可以用xml等等。
(2)這里提供一種C與Java數據交換格式:struct <-> byte[] <-> javaBean
- C不是一門面向對象的語言,但是C有結構體(struct),C一般操作結構體。
- Java是一門面向對象的語言,所以Java一般操作對象。
- 選擇byte數組作為傳輸格式,節省通信成本,沒有多余內容,不過極度依賴接收方與發送方之間的配合,畢竟如果字段錯亂,將導致解析失敗。
2 C語言:struct -> byte[]
#include <stdio.h>
#include <stdlib.h>
#include <mem.h>
struct SS {
int f;
int d;
short g;
}; //結構定義
int main() {
unsigned char *b; // byte 指針
int N, i;
struct SS s = {7, 8, 4}; //聲明一個結構對象並初始化
N = sizeof(struct SS); //結構大小
b = (unsigned char *) malloc(N); //動態分配b
memcpy(b, &s, sizeof(struct SS)); //內容復制
printf("%d", N);
for (int i = 0; i < N; i++) {
if (i > 0)
printf(",");
printf("%d", b[i]);
}
return 0;
}
3 Java語言:byte[] -> JavaBean
(1)方法1:依賴Unsafe類的數組操作接口
public class BytesToBean {
static class SS {
private int f;
private int d;
private short g;
// 省略getter和setter 構造方法 toString方法
}
private static Unsafe unsafe;
static {
try {
Field f = f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args){
//struct SS {
// int f;
// int d;
// short g;
//}; //結構體定義
byte[] bs = new byte[]{4, 0, 0, 0, 7, 0, 0, 0, 8, 0};
int offset = 0;
SS ss = new SS();
Field[] declaredFields = SS.class.getDeclaredFields();
for (Field declaredField : declaredFields) {
Class<?> clazz = declaredField.getType();
switch (clazz.getTypeName()) {
case "int":
int intValue = unsafe.getInt(bs, Unsafe.ARRAY_BYTE_BASE_OFFSET + offset);
unsafe.putInt(ss, unsafe.objectFieldOffset(declaredField), intValue);
break;
case "short":
short shortValue = unsafe.getShort(bs, Unsafe.ARRAY_BYTE_BASE_OFFSET + offset);
unsafe.putShort(ss, unsafe.objectFieldOffset(declaredField), shortValue);
break;
}
offset += getFiledLength(clazz);
}
System.out.println(ss); // SS{f=4, d=7, g=8}
}
private static int getFiledLength(Class clazz){
Object o = Array.newInstance(clazz, 0);
return unsafe.arrayIndexScale(o.getClass());
}
}
(2)方法2:依賴ByteBuffer類的接口
public class BytesToBeans {
static class SS {
private int f;
private int d;
private short g;
// 省略getter和setter 構造方法 toString方法
}
private static Unsafe unsafe;
static {
try {
Field f = f = Unsafe.class.getDeclaredField("theUnsafe");
f.setAccessible(true);
unsafe = (Unsafe) f.get(null);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
byte[] bs = new byte[]{4, 0, 0, 0, 7, 0, 0, 0, 8, 0};
ByteBuffer byteBuffer = ByteBuffer.wrap(bs);
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
SS ss = new SS();
Field[] declaredFields = UnsafeTest.SS.class.getDeclaredFields();
for (Field declaredField : declaredFields) {
Class<?> clazz = declaredField.getType();
switch (clazz.getTypeName()) {
case "int":
int intValue = byteBuffer.getInt();
// unsafe.objectFieldOffset(declaredField) 獲取該字段的偏移量
// Java對象:對象頭 常量池數組 字段 方法
// 請參考:https://www.cnblogs.com/linzhanfly/p/9552910.html
unsafe.putInt(ss, unsafe.objectFieldOffset(declaredField), intValue);
break;
case "short":
short shortValue = byteBuffer.getShort();
unsafe.putShort(ss, unsafe.objectFieldOffset(declaredField), shortValue);
break;
}
}
System.out.println(ss); // SS{f=4, d=7, g=8}
}
}
4 總結
- C與Java傳輸格式為byte數組,總體流程:struct <-> byte[] <-> javaBean。
- C不熟悉
- Java依賴Unsafe的對象字段賦值操作API、反射機制和ByteBuffer的byte數組操作API
最后,這也許不是最好的方案,我也不知道是否有更優秀的方案......