基于JNI的Java串口通信系统的设计与实现
作者:罗尹奇 刘力银
来源:《电脑知识与技术》2017年第34期
作者:罗尹奇 刘力银
来源:《电脑知识与技术》2017年第34期
摘要:为解决在不同平台下Java串口通信问题,设计了一种通用的Java串口通信系统。该系统采用三层体系架构,包含了Java接口调用层、本地接口调用层和本地实现层;Java接口调用层负责定义串口通信的功能函数,本地接口调用层则由JNI(Java Native Interface)技术生成,定义本地代码的函数调用接口,本地实现层利用操作系统API实现串口通信功能。根据该系统设计,以Windows平台为例,实现了在Windows平台下的Java串口通信。通过功能测试实验表明该系统设计能正确地完成串口通信功能。
关键词:JNI(Java Native Interface);本地方法;串口通信;Windows
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2017)34-0051-06
Abstract: In order to solve the problem of Java serial communication in different platforms, a general Java serial communication system is designed in this paper. The syste
m uses three layer architecture, including Java interface layer, native interface layer and a local implementation layer; Java interface layer is responsible for the function definition of serial communication, native interface layer is formed by JNI (Java Native Interface) technique and the function interface to define the local code, local implementation layer using operation API realize the serial communication function. According to the design of the system, this paper takes the Windows platform as an example to realize the Java serial communication under the Windows platform. The function test shows that the design of the system can correctly complete the serial communication function.
Key words: JNI(Java Native Interface); native method;serial communication; Windows
随着物联网技术的不断发展,串口通信技术作为一项十分重要的数据传输手段正得到越来越广泛的应用[1]。在传统的应用领域里,串口设备的访问均是基于C/C++本地代码实现的,虽然具备较高的访问性能,但其跨平台性则相对受限;而Java程序在跨平台方面具备得天独厚的优势,其体系结构无关性正受到越来越多的企业级服务的青睐[2-3]。然而Java的跨
平台特性也为其带来了一定的局限性,部分与平台相关的功能无法得到良好支持[4],在对本地硬件设备访问方面(诸如串口设备等)就是受限情况之一。
JNI技术(Java Native Interface)作为Java访问C/C++本地代码的接口,可以实现对本地动态库的调用,既弥补了Java的不足,同时也兼具了跨平台的优势[3]java知识体系。通过该技术可以将与平台相关的串口通信同跨平台的Java应用结合起来,实现Java程序对串口设备的访问。
1 关键技术简介
1.1 JNI(Java Native Interface)
JNI(Java Native Interface)是Java本地程序接口,属于JDK的一部分[3]。JNI允许运行在Java 虚拟机(JavaVirtual Machine,JVM)上的Java代码操作其他语言(例如C/C++)编写的应用程序和库[5]。同时本地应用程序和库也可以通过JNI来操作JVM内存中的Java对象,实现与Java应用程序共享这些对象[5]。
本地代码在平台属性使用和高性能计算方面具备明显的优势,而JNI技术则将这种优势集成到了Java之中,极大扩展了Java的功能范畴,特别是在处理Java本身不具备的平台属性
和提升Java应用程序性能方面。可以说JNI技术充当了跨平台的Java代码和平台相关的本地代码的通信“桥梁”,实现了两者之间的互操作性。
1.2 串口通信
串口是计算机与外围设备之间的数据传输通道[6],数据通过串口以一位一位按顺序的方式进行传输,其优点是只需一对传输线,大大降低了数据传输的成本,特别适合远距离通信[7]。当前主流的串口标准包含了RS-232、RS-485、RS-422等,这些标准只对接口的电气特性做出规定,而不涉及接插件、电缆或协议,在此基础上用户可以建立自己的高层通信协议。
在当前主流操作系统平台上,串口设备是作为一种设备资源存在的[6],不同的操作系统对设备资源的管理方式各不相同,对串口设备的编程访问也需要依平台而定。在Windows平台下,实现串口通信的方式主要包括平台API函数、MScomm通信组件以及VS2008专门提供的串口通信类SerialPort[6]。
2 系统设计
2.1 整体架构
为了实现系统模块化设计,保证各个模块之间逻辑相对独立以方便后续改进,该系统采用了三层体系架构,即Java接口调用层、本地接口调用层和本地实现层。各层的关系如下:
1) Java接口调用层:定义串口通信的Java接口,该接口一方面提供给应用层代码调用,实现Java程序与串口设备通信;另一方面该接口通过JNI技术产生本地调用接口,实现Java调用接口与本地调用接口一一对应关系。
2) 本地接口调用层:由JNI技术产生与Java接口对应的本地代码(C/C++)接口,该接口规定了本地实现串口通信功能时所必须遵照的调用协议[2]。
3) 本地实现层:利用操作系统API实现串口号获取、打开串口、配置参数、读写串口、关闭串口等通信功能,并对本地调用接口进行封装。
根据各层的关系,该系统的整体架构设计如下:
2.2 Java接口调用层
Java接口调用层实现对串口对象的抽象,将实际物理串口设备抽象成逻辑串口对象,并提供给应用层代码调用;同时为了和本地代码通信,该层定义了调用接口,描述了本地接口应该遵照的调用协议形式。由于Java语言本身是面向对象的,而本地方法(例如C语言)是面向过程的,所以调用接口是按照基本数据传递方式进行设计。Java接口调用层设计如图2所示。
1) SerialPort接口描述了串口对象的基本功能,屏蔽了底层串口通信细节,应用层可以调用该接口实现与串口通信通信;在该接口中定义了串口的打开和关闭、配置串口以及读写串口功能;
2) SerialPortImpl类为SerialPort接口的具体实现,该类中包含了串口对象的波特率、数据位、停止位和校验位属性,同时由于串口设备只能采用独占式打开,因此该类采用了单例设计模式,保证逻辑串口对象和物理串口设备的统一;
3) PortHelper类为调用接口,可以通过JNI技术生成本地调用接口,该接口中的方法均为静态native方法,以便和本地代码(例如C语言)匹配。
2.3 本地接口调用层
本地接口调用层由JNI技术自动生成,该层的函数与上述PortHelper类中的方法形成一一对应关系,从而实现Java虚拟机通过装载动态连接库[4],经JNI间接完成本地代码调用的过程。由于该层接口是对Java调用接口的本地描述,因此当Java调用接口发生改变时,该层需重新生成以保证调用逻辑正确对应。
本地调用层在实现与Java接口调用层通信的同时,还需要实现同本地实现代码之间的调用。为了保持模块的相对独立性,本地调用层不包含具体的串口通信代码,其主要功能是实现基本的数据提取和安全检查,并将处理后的数据传递给本地实现层函数。本地接口调用层设计如下:
1) 本地接口依赖于JNI技术自动生成,是一组本地代码的函数声明,不包含具体的逻辑功能,与PortHelper类中的方法对应。由于采用了基本数据类型传递数据,因此生成的本地接口中的数据类型可以和C语言中的基本数据类型进行关联,并可以通过相应的方法完成数据转换;
2) Java中的数据类型与C语言中的数据类型存在差异,无法在本地代码中直接使用Java数据类型,因此需要对JNI传递而来的数据做适当的提取与格式转化之后才能在本地代
码中使用;
3) 在JVM内存模型中,JVM能够实现对堆内存进行自动垃圾回收,但在本地内存上则无法实现,因此在处理数据的过程中,还需对数据的内存进行管理,以保证不会发生JVM崩溃问题。
2.4 本地实现层
本地实现层负责处理具体的串口逻辑功能。由于串口设备的驱动方式和通信方式与具体的操作系统平台相关,因此本地实现层需要借助具体的操作系统平台API来实现对串口设备的操作。串口设备作为一种独占式访问设备,要求用户程序有且仅有一个实例来获取串口句柄,因此在本地实现层里提供了一个全局串口变量来抽象该设备,通过该变量的使用状态来达到访问互斥的效果。本地实现层设计如下:
在现代操作系统中,物理设备被抽象成各种文件,因此对设备的访问也相应的抽象成了文件访问。而在不同操作系统中,文件模型以及读写文件的IO模型各不一样,故本地实现层的代码需视具体的操作系统平台而定。全局串口变量存在于本地内存中,为了保证其内存运
行正确,不会出现溢出崩溃现象,故设计了一组内存管理函数New_Port和Free_Port,用于对全局串口变量的内存空间开辟和释放的管理。
3 代码实现
3.1 Java接口实现
在Java接口调用层中,PortHelper类为调用接口,也是通过JNI技术生成本地调用接口的模板,因此对该接口的实现成为了系统调用的关键。根据Java调用接口层设计,接口中的方法均为静态native方法,并且传递的参数均为基本数据类型,不涉及复杂对象的传递。同时Java语言本身具备良好的跨平台性,该接口的定义在任意平台下均适用。调用接口的代码如下:
各方法的功能及参数的说明如表2所示。
3.2 本地接口生成
本地接口是基于JNI技术自动生成的,在生成时需要使用不同平台下的JNI[3]。本文选择
了Windows平台作为实验平台,因此需使用该平台下的JNI(%JAVA_HOME%\bin\)。通过调用javah命令将上述PortHelper类生成对应的本地接口文件test_tools_PortHelper.h[2]。该头文件中定义的本地接口与PortHelper类中的静态方法形成了一一对应关系,具体代码如下(省略条件编译代码):
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论