4

java与c通信实现方案

 2 years ago
source link: https://wakzz.cn/2017/12/05/java/java%E4%B8%8Ec%E9%80%9A%E4%BF%A1%E5%AE%9E%E7%8E%B0%E6%96%B9%E6%A1%88/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1、源代码下载

java2c

Java与c通信,最大的问题就是Java数据和c数据的转换问题。最近我做的项目就是java与c通过蓝牙通信,难点就是java数据结构和c结构体数据的转换问题。
通过几天的研究,实际上这个问题还是比较容易解决的,并以java自制了一个自动转换两者数据结构的工具。
Java和c两者通信都是通过流获取以及发送字节,只需要将数据结构解析成对应语言平台的字节数组,以及字节数组组装成对应语言平台的数据结构即可。

Java工具使用方法如下

/**
* 结构体定义
*/
public class TestBean {

// typedef struct Books
// {
// char title[10];
// char name;
// int int_id[2];
// unsigned short int short_id;
// float book_id;
// };
@TXConvertData(index=0,dataType=TXDataEnum.TXChar,arrayLength=10)
private char[] title;
@TXConvertData(index=1,dataType=TXDataEnum.TXChar)
private char name;
@TXConvertData(index=2,dataType=TXDataEnum.TXInt,arrayLength=2)
private int[] int_id;
@TXConvertData(index=3,dataType=TXDataEnum.TXShortInt)
private short short_id;
@TXConvertData(index=4,dataType=TXDataEnum.TXFloat)
private float book_id;
public char[] getTitle() {
return title;
}
public void setTitle(char[] title) {
this.title = title;
}
public char getName() {
return name;
}
public void setName(char name) {
this.name = name;
}
public int[] getInt_id() {
return int_id;
}
public void setInt_id(int[] int_id) {
this.int_id = int_id;
}
public short getShort_id() {
return short_id;
}
public void setShort_id(short short_id) {
this.short_id = short_id;
}
public float getBook_id() {
return book_id;
}
public void setBook_id(float book_id) {
this.book_id = book_id;
}

}
/**
* 与c通信
*/
public class DoTest {

public static void main(String[] args) throws Exception {
// 创建一个数据转换工具对象
TXDataReader<TestBean> reader = new TXDataReader<TestBean>(TestBean.class);

SocketAddress address = new InetSocketAddress("127.0.0.1", 8888);
Socket socket = new Socket();
socket.connect(address);
OutputStream out = socket.getOutputStream();
InputStream in = socket.getInputStream();

TestBean sendData = getBean();
// 将java对象转为c结构体的字节数组并发送
out.write(reader.convertToByte(sendData));
// 获取对应c结构体的字节长度
int len = reader.getDataLength();
byte[] buffer = new byte[len];
in.read(buffer);
// 将c结构体字节数字转为java对象
TestBean recv = reader.convertToObject(buffer);
console(recv);
out.close();
socket.close();
}

private static TestBean getBean(){
TestBean data = new TestBean();
// 字符串最后一个内容后必须是0,即字符串结束符
data.setTitle(new char[]{'h','e','l','l','o','w','o','r','l',0});
data.setName('s');
data.setInt_id(new int[]{123,456});
data.setShort_id((short)32767);
data.setBook_id(3.14f);
return data;
}

private static void console(TestBean data){
System.out.println("title:"+new String(data.getTitle()));
System.out.println("name:"+data.getName());
System.out.println("short_id:"+data.getShort_id());
System.out.println("int_id[0]:"+data.getInt_id()[0]);
System.out.println("int_id[1]:"+data.getInt_id()[1]);
System.out.println("book_id:"+data.getBook_id());
}
}
// TestSocketServer.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <stdio.h>
#include <winsock2.h>
#include <string.h>

#pragma comment(lib,"ws2_32.lib")

// 结构体定义
typedef struct Books
{
char title[10];
char name;
//char test;
int int_id[2];
unsigned short int short_id;
float book_id;
};

int main(int argc, char* argv[])
{
//初始化WSA
WORD sockVersion = MAKEWORD(2, 2);
WSADATA wsaData;
if (WSAStartup(sockVersion, &wsaData) != 0)
{
return 0;
}

//创建套接字
SOCKET slisten = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (slisten == INVALID_SOCKET)
{
printf("socket error !");
return 0;
}

//绑定IP和端口
sockaddr_in sin;
sin.sin_family = AF_INET;
sin.sin_port = htons(8888);
sin.sin_addr.S_un.S_addr = INADDR_ANY;
if (bind(slisten, (LPSOCKADDR)&sin, sizeof(sin)) == SOCKET_ERROR)
{
printf("bind error !");
}

//开始监听
if (listen(slisten, 5) == SOCKET_ERROR)
{
printf("listen error !");
return 0;
}

//循环接收数据
SOCKET sClient;
sockaddr_in remoteAddr;
int nAddrlen = sizeof(remoteAddr);
char revData[255];
while (true)
{
printf("\n等待连接...\n");
sClient = accept(slisten, (SOCKADDR *)&remoteAddr, &nAddrlen);
if (sClient == INVALID_SOCKET)
{
printf("accept error !");
continue;
}
printf("接受到一个连接:%s \r\n", inet_ntoa(remoteAddr.sin_addr));

//接收数据
struct Books revBook;
int ret = recv(sClient, revData, sizeof(Books), 0);
if (ret > 0)
{
for(int i=0;i<sizeof(Books);i++){
printf("%d,",revData[i]);
}
printf("\n");
memcpy(&revBook, revData, sizeof(revBook));
printf("title:%s\n",revBook.title);
printf("name:%c\n",revBook.name);
printf("short_id:%d\n",revBook.short_id);
printf("int_id[0]:%d\n",revBook.int_id[0]);
printf("int_id[1]:%d\n",revBook.int_id[1]);
printf("book_id:%f\n",revBook.book_id);
}

struct Books book;
strcpy_s(book.title, "hello-c1");
book.name = 'a';
book.int_id[0] = 253;
book.int_id[1] = 13;
book.book_id = 22.2;
book.short_id = -34;

char snd_buf[sizeof(book)];
memset(snd_buf, 0, sizeof(book));
memcpy(snd_buf, &book, sizeof(book));

printf("\nlen:%d\n",sizeof(book));
printf("\nlen:%d\n",sizeof(snd_buf));
send(sClient, snd_buf, sizeof(snd_buf), 0);

printf("发送:\n");
for(int i=0;i<sizeof(snd_buf);i++){
printf("%d,",snd_buf[i]);
}

closesocket(sClient);
}

closesocket(slisten);
WSACleanup();
return 0;
}

c控制台输出:

58138980.jpg

java控制台输出:

11698769.jpg

如图,成功自动将java对象转换为c结构体,以及将c结构体转换为java对象。

4、注意事项

方法com.wxtx.java2c.reflect.TXDataReader.needAlign(int, int)的作用是c结构体中的字节对齐功能。默认是按结构体中最长的字段进行字节对齐。例如例子中最长字段时float类型的book_id,所以以4字节对齐。
实际情况中c可能是其他对齐方式,根据需要可以修改该方法。

注解@TXConvertData的使用方式:

@TXConvertData(index=0,dataType=TXDataEnum.TXChar,arrayLength=10)
private char[] title;
  1. index表示被注解的字段在c结构体中的序号(从0开始计数);
  2. dataType表示被注解字段的数据类型,分别有TXDataEnum.TXInt,TXDataEnum.TXShortInt,TXDataEnum.TXChar,TXDataEnum.TXFloat。因为这次项目中涉及到的数据类型就这4种,所以也就没有什么动力在添加double以及其他的数据类型。至于结构体中嵌套结构体的情况,只需要进行递归解析即可,有需求的小伙伴自行修改工具代码吧,哈哈哈;
  3. arrayLength表示被注解字段的数组长度,如果该字段不是数组,则不需要使用该注解属性

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK