`
jacktanlikejava
  • 浏览: 8517 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

Java Applet读写客户端串口——终极篇

 
阅读更多

测试环境:

SDKOracle JRockit for Java version 6, Java Communication for Windows 2.0

OSWINDOWS7

外设:串口条形码扫描枪

ServerTomcat6

看了网上良莠不齐的关于Applet访问串口的文章,总结起来所关注的问题无外乎以下3个:

1. 三个文件(comm.jarjavax.comm.propertieswin32com.dll)到底应该存放在什么目录中?

2. 如何实现代码?

3. Applet到底应该这么部署?

一.关于第一个问题,网上大致是这样写的:

a) javax.comm.properties文件放在$JAVA_HOME/lib目录中;

b) win32comm.dll文件放在$JAVA_HOME/bin目录中;

c) comm.jar文件放在$JAVA_HOME/lib/ext目录中;

先不去讨论这些文件应不应该放在这些目录中,单从可行性方面讨论就不太符合WEB应用程序的做法。首先您不可能预知有多少客户端的存在,就算您预先知道也不可能在每个客户端计算机上部署上述3个文件。好,您说可以提供使用手册引导用户下载文件并按照手册将上述文件部署到指定目录。但是您增加了用户的使用学习成本,用户不是IT专家,将本来应该由开发人员完成的任务转嫁给用户是否合适呢?

要解决这个问题,关键是要测试,测试Applet在运行时是这么加载这些文件的。经过反复的测试,终于搞清楚其中的来龙去脉:

1. javax.comm.properties文件可以丢弃,因为通过编程的方法可以在Applet中动态加载串口驱动程序,所以该文件存不存在无所谓。

2. comm.jar文件是主要的串口访问类库,可以通过在Applet运行时加载(通过ARCHIVE参数指定,后面有详细的例子),所以也没有必要事先部署到客户端计算机上。

3. 最关键的是win32comm.dll文件,该文件是用C写的串(并)口驱动程序,Java通过JNI调该文件中的函数来实现对串(并)口的访问。所以此文件不可或缺。要将该文件部署到客户端只能通过下载的方式实现,即在Applet运行时检查指定目录中是否存在win32comm.dll文件,如果不存在则将服务器端的win32comm.dll文件下载到客户端的指定目录中,最后再动态装载驱动程序。关于win32comm.dll文件到底要部署到什么目录中,经过测试发现该文件只要存在于由java.library.path系统变量指定的任一目录中即可,该系统变量可以通过System.getProperty(“java.library.path”)方法获取。以下是在我的机器中使用该方法获取的值:

C:/Program Files/Java/jrmc-3.1.2-1.6.0/bin;.;C:/Windows/system32;C:/Windows;C:/Program Files/Java/jrockit-R27.6.5-jre1.6.0_14/bin;C:/Windows/system32;C:/Windows;C:/Windows/System32/Wbem;C:/Windows/System32/WindowsPowerShell/v1.0/;C:/Program Files/ATI Technologies/ATI.ACE/Core-Static;C:/Program Files/Toshiba/Bluetooth Toshiba Stack/sys/;C:/Program Files/SecureCRT/;C:/Program Files/MySQL/MySQL Server 5.1/bin;C:/Tomcat6.0/bin;E:/software/java/jdk/ibm_sdk60/bin;C:/MinGW/bin;C:/Program Files/Java/jrmc-3.1.2-1.6.0/bin;

通过上述分析,我们已经理清了三个文件存放位置,接下来就是如何具体的实现代码了。

二.代码实现

a) 下载win32comm.dll文件到客户端:

先看代码的实现:

private static final String LIB_PATH_SUFFIX = "system32";

private static final String DLL_FILE = "win32com.dll";

try {

// 获取加载库时搜索的路径列表

String dirs = System.getProperty("java.library.path");

String[] libs = dirs.split(";");

String libPath = "";

for (String lib : libs) {

if (lib.toLowerCase().endsWith(LIB_PATH_SUFFIX)) {

libPath = lib;

break;

}

}

File dll = new File(libPath, DLL_FILE);

if (!dll.exists()) {

URL url = new URL(super.getCodeBase() + DLL_FILE);

InputStream is = url.openConnection().getInputStream();

FileOutputStream fos = new FileOutputStream(dll);

byte[] buf = new byte[256]; // 读取缓存

int len = 0;

while ((len = is.read(buf)) != -1) {

fos.write(buf, 0, len);

}

fos.flush();

fos.close();

is.close();

System.out.println("创建文件完成[" + dll + "].");

}

} catch (MalformedURLException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

这段代码的主要算法如下:

1. 通过System.getProperty(“java.library.path”)方法获取加载库时搜索的目录字符串,每个目录之间是用分号隔开的;

2. 将目录字符串按分号拆分成字符串数组,然后取其中的任一一个即可。不过我喜欢取“C:/Windows/system32目录;

3. 然后实例化一个File对象,该对象存有指向C:/Windows/system32/win32comm.dll文件的句柄。检测该文件是否存在,如果存在则不做任何处理,否则进行下载处理;

4. 如果需要下载文件,则先通过getCodeBase()方法获取服务器端的根URL对象。然后构造一个指向服务器端win32comm.dll文件的URL对象;

5. 通过URLopenConnection().getInputStream()方法获取InputStream流准备读取;

6. 在客户端实例化一个FileOutputStream对象,并将输出流写入到客户端的文件中(C:/Windows/system32/win32comm.dll),完成文件的下载。其实文件的下载是通过http协议完成的,即httpclient。所以服务器端需要TOMCAT或其他WEB服务器软件。

b) 读取条形码

主要实现Applet3个方法:initstartdestroy方法。先看init方法:

private String driverName = "com.sun.comm.Win32Driver";

public void init() {

try {

System.loadLibrary("win32com");

CommDriver driver = (CommDriver)Class.forName(driverName).newInstance();

driver.initialize();

} catch (Exception e) {

System.err.println(e);

}

}

init方法在Applet加载时执行一次且仅一次。所以init方法中适合装载和初始化驱动程序,即加载win32comm.dll文件。

条码扫描设备与调制解调器不同,调制解调器使用“密步”的通信方式,即请求-应答模式。而条码扫描设备是事件驱动的,只有在扫描了条码之后,才能读取串口的数据,所以使用请求-应答模式肯定不行。为了解决这个问题,Applet必须实现SerialPortEventListener接口,以便在有数据到达时执行特定的方法。另外还需要启动一个线程来等待数据的到达。所以Applet类的签名如下:

public class SerialPortApplet extends JApplet implements Runnable,

SerialPortEventListener {

public void run() {

}

public void serialEvent(SerialPortEvent event) {

}

}

其中serialEvent(SerialPortEvent event)方法就是需要实现的接口方法,当串口有数据到达时,则会执行该方法中的代码。

start方法在init方法执行完毕之后执行,在Applet的整个生命周期中,start方法可以被执行多次。所以start方法可以用来实现寻找可用端口,打开端口,设置端口参数,等待数据到达以及数据处理等代码,代码如下所示:

private CommPortIdentifier portId;

private StringBuilder barcode = new StringBuilder();

private InputStream is;

private boolean over = false; //退出线程的标志

private SerialPort serialPort;

static {

System.setSecurityManager(null); //禁用安全管理器(必须写)

}

public void start() {

Enumeration ports = CommPortIdentifier.getPortIdentifiers();

while (ports.hasMoreElements()) {

portId = (CommPortIdentifier) ports.nextElement();

if (portId.getPortType() == CommPortIdentifier.PORT_SERIAL) { // 是串口

if (portId.getName().equals("COM1")) {

break;

}

}

}

try {

serialPort = (SerialPort) portId.open("App1",2000); // 打开端口

is = serialPort.getInputStream();

serialPort.addEventListener(this); // 注册监听器

serialPort.notifyOnDataAvailable(true); // 数据达到时发出通知

serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); // 设置端口参数

} catch (PortInUseException e) {

System.err.println(e);

} catch (IOException e) {

System.err.println(e);

} catch (TooManyListenersException e) {

System.err.println(e);

} catch (UnsupportedCommOperationException e) {

System.err.println(e);

}

// 启动线程

Thread t = new Thread(this);

t.start();

try {

t.join();// 等待线程结束

} catch (InterruptedException e) {

}

System.out.println("barcode[" + barcode + "]");

}

线程接口的实现和监听器接口的实现代码如下所示:

public void run() {

while (!over) {

}

try {

if (is != null) {

is.close();

}

if (serialPort != null) {

serialPort.close();

}

} catch (IOException e) {

System.out.println(e);

}

}

public void serialEvent(SerialPortEvent event) {

switch (event.getEventType()) {

case SerialPortEvent.DATA_AVAILABLE: //数据到达时执行

try {

while (true) {

int b = is.read(); // 如果读取不到数据则会阻塞

if (b == 10 || b == 13) { // 如果读到回车或换行则表示读取完成

over = true;

break;

} else {

barcode.append(new String(new byte[] { (byte) b }));

}

}

} catch (IOException e) {

System.err.println(e);

}

}

}

destroy方法在Applet销毁时执行且执行一次,所以可以该方法中编写资源释放的代码,代码实现如下:

public void destroy() {

try {

if (is != null) {

is.close();

}

if (serialPort != null) {

serialPort.close();

}

} catch (IOException e) {

System.out.println(e);

}

}

c) HTML页面(index.html)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=gb2312" />

<title></title>

</head>

<body>

<!--"CONVERTED_APPLET"-->

<!-- HTML CONVERTER -->

<OBJECT classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93"

WIDTH = "250" HEIGHT = "140" codebase="http://java.sun.com/update/1.6.0/jinstall-6u17-windows-i586.cab#Version=6,0,0,4">

<PARAM NAME = CODE VALUE = "org.oakman.applets.SerialPortApplet.class" >

<PARAM NAME = CODEBASE VALUE = "." >

<PARAM NAME = ARCHIVE VALUE = "comm.jar, serial_port.jar" >

<PARAM NAME="type" VALUE="application/x-java-applet;version=1.6">

<PARAM NAME="scriptable" VALUE="false">

<COMMENT>

<EMBED type="application/x-java-applet;version=1.6" CODE = "org.oakman.applets.SerialPortApplet.class" CODEBASE = "." ARCHIVE = " comm.jar,serial_port.jar" WIDTH = "250" HEIGHT = "140" scriptable=false pluginspage="http://java.sun.com/products/plugin/index.html#download"><NOEMBED>

</NOEMBED>

</EMBED>

</COMMENT>

</OBJECT>

<!--"END_CONVERTED_APPLET"-->

</body>

</html>

其中比较重要的是ARCHIVE参数,可以将Applet需要用到的所有jar类库都在这个参数中进行设置,多个jar文件之间用逗号进行分隔。如果客户端没有安装JRE,则首先会要求用户下载JRE,正常情况下,下载JRE需要10分钟左右的时间。

经过上述步骤,我们完成了所有需要实现的代码。将所有Java代码打包成jar文件,然后和html文件一起部署到TOMCAT6中,启动,一切正常,然后非常高兴的打开浏览器,输入url,双眼充满期望的等待令人兴奋的一幕。不过……,报错了!非常的沮丧!错误提示没有访问权限!!郁闷中……

三.部署

Java号称是最安全的,所以Applet作为在网络上可以任意传播的小应用程序当然更加需要安全。所以在默认情况下Applet只能运行在JVM的沙箱中。不能访问客户端的任何资源,包括文件系统的读写,网络套接字等。所以出现上述错误理所当然,反而证明了Java的确非常的安全。

为了走出沙箱,我们必须对Applet进行签名:

1. 创建密钥:

使用如下命令进行密钥的创建,这里使用RSA算法而不是Java默认的DSA算法

keytool -genkey -alias oakman -keyalg RSA

运行时会要求您输入密钥库的口令,并要求您输入名字,组织和位置等信息。填好后就会产生密钥(密钥文件在用户目录中,找.keystore文件)。

2. CA订购签名证书:

为了从CA订购签名证书,你需要从密钥库中导出证书签名申请(CSR文件)。使用如下命令

keytool -certreq -alias oakman -file oakman.csr

该命令会在当前目录中产生一个oakman.csr文件,然后你可以用这个文件以及证明你身份的证明或证件以及数KRMBCA那里申请你的证书(比较常见的CAVerisign)。如果申请成功,CA会给你一个BASE64编码证书,你就可以把它导入到密钥库中给自己编写的Applet进行签名了,导入命令如下:

keytool -import -alias oakman -file oakman.cer

其中oakman.cer就是CA给你的证书。

3. Appletjar文件进行签名:

已经将CA的证书导入到密钥库中,那么就可以对自己编写的Applet进行签名了(当然,先要打包成JAR包),使用如下命令:

jarsigner serial_port.jar oakman

jarsigner comm.jar oakman

Applet所有用到的jar文件都要进行签名。

经过上述步骤,我们的Applet就可以走出沙箱了,可以访问客户端的任何资源,包括文件系统,外设以及网络套接字等。将条形码扫码枪接上我笔记本的串口,找了一本书,对着书上的条码一扫,哔!!扫描成功,接着在Java控制台出现一串数字,OK!终于搞定。(注:由于我笔记本没有串口,所以通过USB转串口来模拟出串口,可能是由于驱动程序的原因,如果在没有关闭串口的情况关闭IE浏览器,则操作系统必然死机,只能手动重启操作系统。)

当然,作为测试,我们没有必要花数KRMBCA那里申请证书,所以可以将步骤2省略。在生成密钥库之后直接对JAR文件进行签名。

四.部署目录

以下为我机器上TOMCAT中应用程序的部署目录:

pay

|-- WEB-INF

|-- web.xml

|-- comm.jar

|-- serial_port.jar

|-- index.html

分享到:
评论

相关推荐

    Applet读取客户端串口数据实例

    本文将详细介绍如何利用Java Applet读取客户端的串口数据,并将其发送至服务器进行处理。Applet是一种可嵌入到网页中的小型Java应用程序,能够在客户端浏览器中运行。这种特性使得Applet非常适合于读取客户端本地...

    Java Applet研究与应用——综合测评系统

    在“Java Applet研究与应用——综合测评系统”这个项目中,我们主要探讨的是如何利用Java Applet技术来构建一个在线的、能够进行多方面评估的系统。 Java Applet的工作原理是,它被嵌入到HTML页面中,由Java虚拟机...

    网页上Applet用javacomm20读取客户端串口

    标题中的“网页上Applet用javacomm20读取客户端串口”指的是在Web页面中使用Java Applet通过javacomm20这个库来访问客户端计算机的串行通信端口(COM口)。这通常用于实现远程控制、数据传输或者设备交互等应用场景...

    JAVA获取客户端MAC,web获取客户端MAC,Applet获取客户端MAC

    JAVA获取客户端MAC,web获取客户端MAC,Applet获取客户端MAC;JAVAweb开发;服务器端很难获取到客户端的MAC受路由器等....获取的到不正确或者被过滤掉;小弟,无奈写了一个客户端的Applet来获取客户端MAC然后再在传...

    Applet与Javascript的对话——让你的Javascript代码和Java Applet融洽地合作.pdf

    标题中的“Applet与Javascript的对话——让你的Javascript代码和Java Applet融洽地合作”指的是一种技术实现,即如何让JavaScript与Java Applet在Web应用中进行交互。这两种技术在早期Web开发中常常结合使用,以利用...

    java applet读取串口实例.rar

    压缩包为一个Myeclipse工程,演示了使用JavaApplet读取串口书的条形码的整个过程 详情见:http://hi.baidu.com/xxgshxs/home 在myeclipse中选择导入已有工程,然后选择xxx文件夹导入,发布后,浏览器输入 ...

    JavaApplet_javaapplet小程序_java_

    由于Applet可能来自不可信的网络源,因此Java引入了安全沙箱模型,限制了Applet的权限,如读写本地文件、访问网络资源等。这降低了恶意Applet对用户系统的潜在威胁。 六、JavaApplet的局限性与替代方案 随着Web...

    网页java applet串口通信读取RFID电子标签读写

    Java Applet串口通信允许Applet与本地计算机的硬件设备进行交互,如通过串行端口与RFID读卡器建立连接,从而读取或写入RFID标签的信息。 RFID技术是一种无线通信技术,它通过无线电频率信号识别特定目标并读写相关...

    javaapplication和javaapplet的区别

    - **运行位置**:Java Application通常在本地机器上运行,而Java Applet则通常部署在远程服务器上,根据客户端的请求下载并执行。 - **安全性**:由于Java Applet的运行环境较为受限,它通常被设计得更加安全,以...

    APPLET小动画——JAVA

    【标题】"APPLET小动画——JAVA"是一个与Java编程相关的学习项目,主要涉及的是Java Applet技术。Applet是Java平台早期用于在Web浏览器中展示交互式内容的小程序,它允许开发者创建动态、交互式的网页元素。在这个...

    java applet 简单案例

    Java Applet是一种由Java编写的客户端应用程序,它可以在支持Java的Web浏览器中运行。Applets通常用于创建交互式的网页元素,如动画、游戏或者复杂的用户界面。由于它们是用Java编写,所以具有“一次编写,到处运行...

    JavaApplet实例及教程

    Java+Applet实例讲解(分为1和2两部分)、Java+Applet实例编程、java+Applet登录、JavaApplet教程.pdf、JAVA_applet应用实例.ppt、JavaApplet编程技巧实例专辑、javaapplet例子.doc、Applet类.doc等教程。...

    java Application与java Applet的区别

    Java Application在权限上较为宽松,可以执行读/写文件等操作,而Java Applet受到沙盒模型的严格限制,禁止对本地文件系统进行读写操作,以保护用户数据安全。这一限制虽然牺牲了一定的功能性,但极大地增强了Web...

    JavaApplet与JavaScript

    ### JavaApplet与JavaScript #### 6.1 JavaApplet ##### 6.1.1 JavaApplet概述 **Applet 类层次结构:** JavaApplet 的基础是 `java.applet.Applet` 类,它是从 `java.awt.Panel` 继承而来的一个特殊的图形组件。...

    javaapplet实例讲解

    为了缓解这个问题,Java引入了安全沙箱模型,限制了Applet的一些操作,比如读写文件、网络通信等。然而,即使如此,Applet的安全性仍不理想,这也是它逐渐被淘汰的原因之一。 7. **替代技术**: 如今,JavaScript...

    简单的java applet图像移动重画实例

    Java Applet是Java技术在早期Web开发中的一种应用方式,它允许开发者创建可以在浏览器中运行的小型应用程序。在这个“简单的java applet图像移动重画实例”中,我们将深入探讨Java Applet的基础知识,以及如何实现...

    Java applet程序设计

    本篇文章将深入探讨Java Applet的基础知识、编程实践以及与JavaScript的交互。 首先,我们要理解Java Applet的基本概念。Java Applet是Java平台的一个子集,它是专门为网络环境设计的。Applet由Java编写的类组成,...

    115个Java面试题和答案——终极(下)

    ### 异常处理 ...通过以上总结,我们不仅了解了Java中的异常处理机制以及Applet的相关概念,还深入探讨了Applet的安全性和执行环境等问题。这对于理解Java的高级特性以及编写安全可靠的Web应用程序至关重要。

    用java applet方式实现服务器推技术

    在这个场景中,我们将探讨如何使用Java Applet来实现服务器推(Server-Side Push)技术,这是一个使得服务器能够主动向客户端发送数据而非等待客户端请求的机制,特别适用于实时性要求高的应用,如温度监控。...

Global site tag (gtag.js) - Google Analytics