java与c通信实现方案

1、源代码下载

java2c

2、介绍

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

3、代码

Java工具使用方法如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/**
* 结构体定义
*/
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());
}
}

c代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
// 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控制台输出:

java控制台输出:

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

4、注意事项

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

注解@TXConvertData的使用方式:

1
2
@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表示被注解字段的数组长度,如果该字段不是数组,则不需要使用该注解属性