此博客用到JNI+UDP
最近在和某研究所联调程序,遇到很多问题,目前解决大半,快来总结一下。题外话:程序员千万别去研究所,因为那里气氛比殡仪馆还压抑。
大致需求:
1. 某研究所提供仿真软件(C编写),可以通过udp访问执行,并通过udp返回结果。传递参数皆为结构体(C struct)
2. 利用java web编写网站,实现调用仿真软件并展现结果。
看到这个需求,认为简单的udp就可以实现。但是参数的传递将是难题。
先来看下java udp实现
/**
* UDP服务类.
* @author liukang
*/
public class UdpServerSocket {
private byte[] buffer = new byte[1024];
private DatagramSocket ds = null;
private DatagramPacket packet = null;
private InetSocketAddress socketAddress = null;
private String orgIp;
/**
* 构造函数,绑定主机和端口.
* @param host 主机
* @param port 端口
* @throws Exception
*/
public UdpServerSocket(String host, int port) throws Exception {
socketAddress = new InetSocketAddress(host, port);
ds = new DatagramSocket(socketAddress);
System.out.println("服务端启动!");
}
public final String getOrgIp() {
return orgIp;
}
/**
* 设置超时时间,该方法必须在bind方法之后使用.
* @param timeout 超时时间
* @throws Exception
*/
public final void setSoTimeout(int timeout) throws Exception {
ds.setSoTimeout(timeout);
}
/**
* 获得超时时间.
* @return 返回超时时间.
* @throws Exception
*/
public final int getSoTimeout() throws Exception {
return ds.getSoTimeout();
}
/**
* 绑定监听地址和端口.
* @param host 主机IP
* @param port 端口
* @throws SocketException
*/
public final void bind(String host, int port) throws SocketException {
socketAddress = new InetSocketAddress(host, port);
ds = new DatagramSocket(socketAddress);
}
/**
* 接收数据包,该方法会造成线程阻塞.
* @return 返回接收的数据串信息
* @throws IOException
* @throws ClassNotFoundException
*/
public final byte[] receive() throws IOException, ClassNotFoundException {
packet = new DatagramPacket(buffer, buffer.length);
ds.receive(packet);
orgIp = packet.getAddress().getHostAddress();
System.out.println(orgIp);
return buffer;
}
/**
* 将响应包发送给请求端.
* @param bytes 回应报文
* @throws IOException
*/
public final void response(String info) throws IOException {
System.out.println("客户端地址 : " + packet.getAddress().getHostAddress()
+ ",端口:" + packet.getPort());
DatagramPacket dp = new DatagramPacket(buffer, buffer.length, packet
.getAddress(), packet.getPort());
dp.setData(info.getBytes());
ds.send(dp);
}
/**
* 设置报文的缓冲长度.
* @param bufsize 缓冲长度
*/
public final void setLength(int bufsize) {
packet.setLength(bufsize);
}
/**
* 获得发送回应的IP地址.
* @return 返回回应的IP地址
*/
public final InetAddress getResponseAddress() {
return packet.getAddress();
}
/**
* 获得回应的主机的端口.
* @return 返回回应的主机的端口.
*/
public final int getResponsePort() {
return packet.getPort();
}
/**
* 关闭udp监听口.
*/
public final void close() {
try {
ds.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
} package com.platform.net;
import java.io.*;
import java.net.*;
import java.nio.ByteOrder;
import struct.JavaStruct;
import com.platform.report.receive.OneSend;
import com.platform.report.receive.Response1;
import com.platform.report.send.DATA1;
import com.platform.report.send.Struct1;
import com.platform.util.ByteUtil;
/**
* UDP客户端程序,用于对服务端发送数据,并接收服务端的回应信息.
* @author liukang
*/
public class UdpClientSocket {
//接收buffer
private byte[] buffer = new byte[1200];
private DatagramSocket ds = null;
/**
* 构造函数,创建UDP客户端
* @throws Exception
*/
public UdpClientSocket() throws Exception {
//ds = new DatagramSocket();
ds = new DatagramSocket(21168);
}
/**
* 设置超时时间,该方法必须在bind方法之后使用.
* @param timeout 超时时间
* @throws Exception
*/
public final void setSoTimeout(final int timeout) throws Exception {
ds.setSoTimeout(timeout);
}
/**
* 获得超时时间.
* @return 返回超时时间
* @throws Exception
*/
public final int getSoTimeout() throws Exception {
return ds.getSoTimeout();
}
public final DatagramSocket getSocket() {
return ds;
}
/**
* 向指定的服务端发送数据信息.
* @param host 服务器主机地址
* @param port 服务端端口
* @param bytes 发送的数据信息
* @return 返回构造后俄数据报
* @throws IOException
*/
public final DatagramPacket send(final String host, final int port,
final byte[] bytes) throws IOException {
DatagramPacket dp = new DatagramPacket(bytes, bytes.length, InetAddress
.getByName(host), port);
ds.send(dp);
return dp;
}
/**
* 接收从指定的服务端发回的数据.
* @param lhost 服务端主机
* @param lport 服务端端口
* @return 返回从指定的服务端发回的数据.
* @throws Exception
*/
public final byte[] receive(final String lhost, final int lport)
throws Exception {
DatagramPacket dp = new DatagramPacket(buffer, buffer.length);
ds.receive(dp);
//String info = new String(dp.getData(), 0, dp.getLength());
return buffer;
}
/**
* 关闭udp连接.
*/
public final void close() {
try {
ds.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
udp的问题解决了,那么难点就在与如何传值。从上面代码可以看出,udp传值都是利用byte数组。
但是如何拼装出对应的byte数组却成了问题。
熟悉网络通信的同学可能知道,网络传输字节存在大端小端问题,并且网络字节序可能和存储字节序不同的问题。
大端小端模式 http://blog.csdn.net/hackbuteer1/article/details/7722667
让java去操作字节有点勉为其难,首先看一下java各类型所占的字节数:
Int: 4 字节Short: 2字节Long: 8字节Byte: 1字节Char: 2字节Float: 4字节Double: 8字节
C数据类型和所占字节数:
16位编译器
char :1个字节 char*(即指针变量): 2个字节 short int : 2个字节
int: 2个字节 unsigned int : 2个字节 float: 4个字节
double: 8个字节 long: 4个字节 long long: 8个字节
unsigned long: 4个字节
32位编译器
char :1个字节 char*(即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器)
short int : 2个字节 int: 4个字节 unsigned int : 4个字节
float: 4个字节 double: 8个字节 long: 4个字节long long: 8个字节 unsigned long: 4个字节
64位编译器
char :1个字节 char*(即指针变量): 8个字节 short int : 2个字节 int: 4个字节
unsigned int : 4个字节 float: 4个字节 double: 8个字节 long: 8个字节long long: 8个字节 unsigned long: 8个字节
对于不同编译器的C每种类型的数据所占字节数不同,在不考虑高低字节的问题,如果用java编写扩展性不好,且需要逐个字节去对齐。
遇到这个问题,想到两种方法。
一种是文件缓存,利用文件做数据交换的媒介,例如webservice之间利用xml调用
另一种方式就是不用java做通信这件事情,而是用C,让C和C之间通信,如果更换C编译器只需要重新编译动态链接库即可。
第一种实现更简单,但研究所不同意更改任何一行代码。这就是说好的合作!
JNI定义方法:
1.首先定义java类如下:注意加载动态链接库
public class Model {
static{
System.load("/SimulationPlatform/src/main/java/com/platform/jni/model.dylib");
}
public native void model1(DATA1 data);
}2.javac编译形成class文件javac -classpath ~/Documents/phpworkspace/SimulationPlatform/src/main/java/ Model.java
3.javah形成c语言头文件
javah -classpath ~/Documents/phpworkspace/SimulationPlatform/src/main/java/ com.platform.jni.Model
4.编写c语言程序,各类型如何转换都在程序上
gcc -fPIC -D\_REENTRANT -I /Library/Java/JavaVirtualMachines/jdk1.7.0_75.jdk/Contents/Home/include/ -I /Library/Java/JavaVirtualMachines/jdk1.7.0_75.jdk/Contents/Home/include/darwin -c model.c
JNIEXPORT void JNICALL Java_com_platform_jni_Model_model1
(JNIEnv *env, jobject obj1, jobject obj2){
struct DATA1 data2;
int len;
int i,j;
jclass class = (*env)->GetObjectClass(env,obj2);//获得class
printf("get class right\n");
/*获取char开始 s1*/
jfieldID s1id = (*env)->GetFieldID(env,class,"s1","C");
jchar s1 = (*env)->GetCharField(env,obj2,s1id);
data2.s1 = s1-'0';
/*获取char结束*/
/*获取float开始 speed*/
jfieldID speedid = (*env)->GetFieldID(env,class,"speed","F");
jfloat speed = (*env)->GetFloatField(env,obj2,speedid);
data2.speed = speed;
printf("%f\n",speed);
/*获取float结束*/
/*获取float开始 ang*/
jfieldID angid = (*env)->GetFieldID(env,class,"ang","F");
jfloat ang = (*env)->GetFloatField(env,obj2,angid);
data2.ang = ang;
printf("%f\n",ang);
/*获取float结束*/
/*获取float开始 fre*/
jfieldID freid = (*env)->GetFieldID(env,class,"fre","F");
jfloat fre = (*env)->GetFloatField(env,obj2,freid);
data2.fre = fre;
printf("%f\n",fre);
/*获取float结束*/
/*获取float开始 bre*/
jfieldID breid = (*env)->GetFieldID(env,class,"bre","F");
jfloat bre = (*env)->GetFloatField(env,obj2,breid);
data2.bre = bre;
printf("%f\n",bre);
/*获取float结束*/
/*获取float开始 cre*/
jfieldID creid = (*env)->GetFieldID(env,class,"cre","F");
jfloat cre = (*env)->GetFloatField(env,obj2,creid);
data2.cre = cre;
printf("%f\n",cre);
/*获取float结束*/
/*获取float开始 distence*/
jfieldID distenceid = (*env)->GetFieldID(env,class,"distence","F");
jfloat distence = (*env)->GetFloatField(env,obj2,distenceid);
data2.distence = distence;
printf("%f\n",distence);
/*获取float结束*/
/*获取float开始 ang1*/
jfieldID ang1id = (*env)->GetFieldID(env,class,"ang1","F");
jfloat ang1 = (*env)->GetFloatField(env,obj2,ang1id);
data2.ang1 = ang1;
printf("%f\n",ang1);
/*获取float结束*/
/*获取float开始 ang2*/
jfieldID ang2id = (*env)->GetFieldID(env,class,"ang2","F");
jfloat ang2 = (*env)->GetFloatField(env,obj2,ang2id);
data2.ang2 = ang2;
printf("%f\n",ang2);
/*获取float结束*/
/*获取float开始 time*/
jfieldID timeid = (*env)->GetFieldID(env,class,"time","F");
jfloat ftime = (*env)->GetFloatField(env,obj2,timeid);
data2.time = ftime;
printf("%f\n",ang2);
/*获取float结束*/
/*获取float开始 cy1*/
jfieldID cy1id = (*env)->GetFieldID(env,class,"cy1","F");
jfloat cy1 = (*env)->GetFloatField(env,obj2,cy1id);
data2.cy1 = cy1;
printf("%f\n",cy1);
/*获取float结束*/
/*获取float开始 ss*/
jfieldID ssid = (*env)->GetFieldID(env,class,"ss","F");
jfloat ss = (*env)->GetFloatField(env,obj2,ssid);
data2.ss = ss;
printf("%f\n",ss);
/*获取float结束*/
/*获取float开始 ang3*/
jfieldID ang3id = (*env)->GetFieldID(env,class,"ang3","F");
jfloat ang3 = (*env)->GetFloatField(env,obj2,ang3id);
data2.ang3 = ang3;
printf("%f\n",ang3);
/*获取float结束*/
/*获取float开始 ang4*/
jfieldID ang4id = (*env)->GetFieldID(env,class,"ang4","F");
jfloat ang4 = (*env)->GetFloatField(env,obj2,ang4id);
data2.ang4 = ang4;
printf("%f\n",ang4);
/*获取float结束*/
/*获取char开始 type1*/
jfieldID type1id = (*env)->GetFieldID(env,class,"type1","C");
jchar type1 = (*env)->GetCharField(env,obj2,type1id);
data2.type1 = type1-'0';
/*获取char结束*/
/*获取char开始 type2*/
jfieldID type2id = (*env)->GetFieldID(env,class,"type2","C");
jchar type2 = (*env)->GetCharField(env,obj2,type2id);
data2.type2 = type2-'0';
/*获取char结束*/
/*获取char开始 type3*/
jfieldID type3id = (*env)->GetFieldID(env,class,"type3","C");
jchar type3 = (*env)->GetCharField(env,obj2,type3id);
data2.type3 = type3-'0';
/*获取char结束*/
/*获取char开始 len1*/
jfieldID len1id = (*env)->GetFieldID(env,class,"len1","C");
jchar len1 = (*env)->GetCharField(env,obj2,len1id);
data2.len1 = len1-'0';
/*获取char结束*/
/*获取char开始 len2*/
jfieldID len2id = (*env)->GetFieldID(env,class,"len2","C");
jchar len2 = (*env)->GetCharField(env,obj2,len2id);
data2.len2 = len2-'0';
/*获取char结束*/
/*获得char数组开始 file1*/
jfieldID file1id = (*env)->GetFieldID(env,class,"file1","[C");//获得属性
jcharArray file1array = (jcharArray)(*env)->GetObjectField(env,obj2,file1id);//获得参数值
len = (*env)->GetArrayLength(env,file1array);
jchar file1[len];//char数组
(*env)->GetCharArrayRegion(env,file1array,0,len,file1);
for( i = 0 ;i<len;i++){
printf("%c\n",file1[i]);
data2.file1[i] = file1[i];
}
/*获得char数组结束*/
/*获得char数组开始 file2*/
jfieldID file2id = (*env)->GetFieldID(env,class,"file2","[C");//获得属性
jcharArray file2array = (jcharArray)(*env)->GetObjectField(env,obj2,file2id);//获得参数值
len = (*env)->GetArrayLength(env,file2array);
jchar file2[len];//char数组
(*env)->GetCharArrayRegion(env,file2array,0,len,file2);
for( i = 0 ;i<len;i++){
printf("%c\n",file2[i]);
data2.file2[i] = file2[i];
}
/*获得char数组结束*/
/**发送开始**/
struct sockaddr_in out;
memset(&out,0,sizeof(out));
out.sin_family = AF_INET;
out.sin_port = htons(PORT);
out.sin_addr.s_addr = inet_addr(IP);
int s;
len = sizeof(struct sockaddr_in);
s = socket(AF_INET,SOCK_DGRAM,0);
if(s == -1){
printf("can not create socket\n");
}
int flag = sendto(s,(char*)&data2,sizeof(data2),0,(struct sockaddr *)&out,len);
if(flag == -1){
printf("socket wrong!\n");
}
close(s);
/**发送结束**/
}5.形成动态链接库,我用的是Mac所以形成dylib
gcc -shared model.o -o model1.dylib
动态链接库形成之后,就可以通过调用native方法来发送数据。再通过java接收即可。
版权声明:本文为liukanglucky原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。