Java Applet读写client串口——终极篇
測试环境:
SDK:Oracle JRockit for Java version 6, Java
Communication for Windows 2.0
OS:WINDOWS7
外设:串口条形码扫描枪
Server:Tomcat6
看了网上良莠不齐的关于Applet訪问串口的文章,总结起来所关注的问题无外乎下面3个:
1.
三个文件(comm.jar、javax.comm.properties和win32com.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应用程序的做法。首先您不可能预知有多少client的存在,就算您预先知道也不可能在每一个client计算机上部署上述3个文件。好,您说能够提供使用手冊引导用户下载文件并依照手冊将上述文件部署到指定文件夹。可是您添加了用户的使用学习成本,用户不是IT专家,将本来应该由开发者完毕的任务转嫁给用户是否合适呢?
要解决问题,关键是要測试,測试Applet在执行时是这么载入这些文件的。经过重复的測试,最终搞清楚当中的来龙去脉:
1.
javax.comm.properties文件能够丢弃,由于通过编程的方法能够在Applet中动态载入串口驱动程序,所以该文件存不存在无所谓。
2.
comm.jar文件是基本的串口訪问类库,能够通过在Applet执行时载入(通过ARCHIVE參数指定,后面有具体的样例),所以也没有必要事先部署到client计算机上。
3.
最关键的是win32comm.dll文件,该文件是用C写的串(并)口驱动程序,Java通过JNI调该文件里的函数来实现对串(并)口的訪问。所以此文件不可或缺。要将该文件部署到client仅仅能通过下载的方式实现,即在Applet执行时检查指定文件夹中是否存在win32comm.dll文件,假设不存在则将server端的win32comm.dll文件下载到client的指定文件夹中,最后再动态装载驱动程序。关于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文件到client:
先看代码的实现:
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()方法获取server端的根URL对象。然后构造一个指向server端win32comm.dll文件的URL对象;
5.
通过URL的openConnection().getInputStream()方法获取InputStream流准备读取;
6.
在client实例化一个FileOutputStream对象,并将输出流写入到client的文件里(C:/Windows/system32/win32comm.dll),完毕文件的下载。事实上文件的下载是通过http协议完毕的,即httpclient。所以server端须要TOMCAT或其它WEBserver软件。
b)
读取条形码
主要实现Applet的3个方法:init、start和destroy方法。先看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方法能够用来实现寻找可用port,打开port,设置port參数,等待数据到达以及数据处理等代码,代码例如以下所看到的:
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); //
打开port
is = serialPort.getInputStream();
serialPort.addEventListener(this); // 注冊监听器
serialPort.notifyOnDataAvailable(true); // 数据达到时发出通知
serialPort.setSerialPortParams(9600, SerialPort.DATABITS_8,SerialPort.STOPBITS_1, SerialPort.PARITY_NONE); //
设置port參数
}
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文件之间用逗号进行分隔。假设client没有安装JRE,则首先会要求用户下载JRE,正常情况下,下载JRE须要10分钟左右的时间。
经过上述步骤,我们完毕了全部须要实现的代码。将全部Java代码打包成jar文件,然后和html文件一起部署到TOMCAT6中,启动,一切正常,然后很高兴的打开浏览器,输入url,双眼充满期望的等待令人兴奋的一幕。只是……,报错了!很的沮丧!错误提示没有訪问权限!!郁闷中……
三.部署
Java号称是最安全的,所以Applet作为在网络上能够随意传播的小应用程序当然更加须要安全。所以在默认情况下Applet仅仅能执行在JVM的沙箱中。不能訪问client的不论什么资源,包含文件系统的读写,网络套接字等。所以出现上述错误理所当然,反而证明了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文件,然后你能够用这个文件以及证明你身份的证明或证件以及数K的RMB到CA那里申请你的证书(比較常见的CA有Verisign)。假设申请成功,CA会给你一个BASE64编码证书,你就能够把它导入到密钥库中给自己编写的Applet进行签名了,导入命令例如以下:
keytool -import -alias oakman -file oakman.cer
当中oakman.cer就是CA给你的证书。
3.
对Applet的jar文件进行签名:
已经将CA的证书导入到密钥库中,那么就能够对自己编写的Applet进行签名了(当然,先要打包成JAR包),使用例如以下命令:
jarsigner serial_port.jar oakman
jarsigner comm.jar oakman
Applet全部用到的jar文件都要进行签名。
经过上述步骤,我们的Applet就能够走出沙箱了,能够訪问client的不论什么资源,包含文件系统,外设以及网络套接字等。将条形码扫码枪接上我笔记本的串口,找了一本书,对着书上的条码一扫,哔!!扫描成功,接着在Java控制台出现一串数字,OK!最终搞定。(注:因为我笔记本没有串口,所以通过USB转串口来模拟出串口,可能是因为驱动程序的原因,假设在没有关闭串口的情况关闭IE浏览器,则操作系统必定死机,仅仅能手动重新启动操作系统。)
当然,作为測试,我们没有必要花数K的RMB去CA那里申请证书,所以能够将步骤2省略。在生成密钥库之后直接对JAR文件进行签名。
四.部署文件夹
下面为我机器上TOMCAT中应用程序的部署文件夹:
pay
|--
WEB-INF
|--
web.xml
|--
comm.jar
|--
serial_port.jar
|--
index.html
Java Applet读写client串口——终极篇的更多相关文章
- 115个Java面试题和答案——终极列表(下)
第一篇讨论了面向对象编程和它的特点,关于Java和它的功能的常见问题,Java的集合类,垃圾收集器,本章主要讨论异常处理,Java小应用程序,Swing,JDBC,远程方法调用(RMI),Servle ...
- 115个Java面试题和答案——终极列表
from http://www.importnew.com/10980.html#collection http://www.importnew.com/11028.html 下面的章节分为上下两篇, ...
- 115个Java面试题和答案——终极列表(下)【转】
第一篇讨论了面向对象编程和它的特点,关于Java和它的功能的常见问题,Java的集合类,垃圾收集器,本章主要讨论异常处理,Java小应用程序,Swing,JDBC,远程方法调用(RMI),Servle ...
- Java快速入门-03-小知识汇总篇(全)
Java快速入门-03-小知识汇总篇(全) 前两篇介绍了JAVA入门的一系小知识,本篇介绍一些比较偏的,说不定什么时候会用到,有用记得 Mark 一下 快键键 常用快捷键(熟记) 快捷键 快捷键作用 ...
- [转]115个Java面试题和答案——终极列表(下)
第一篇讨论了面向对象编程和它的特点,关于Java和它的功能的常见问题,Java的集合类,垃圾收集器,本章主要讨论异常处理,Java小应用程序,Swing,JDBC,远程方法调用(RMI),Servle ...
- Oracle EBS Java Applet报错:找不到类
Oracle EBS Home Page可以打开,但是无法打开EBS的Form,查看Java控制台,有错误报出. java控制台报错,如下: Java Plug-in 1.6.0_07 使用 JRE ...
- Java 面试知识点解析(六)——数据库篇
前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...
- Java 面试知识点解析(七)——Web篇
前言: 在遨游了一番 Java Web 的世界之后,发现了自己的一些缺失,所以就着一篇深度好文:知名互联网公司校招 Java 开发岗面试知识点解析 ,来好好的对 Java 知识点进行复习和学习一番,大 ...
- 解决HMC在IE浏览器无法登录的问题(Java Applet的使用问题)
管理IBM的小型机必须要用到HMC(Hardware Management Console),有时候在使用测试环境使用的时候我们会把HMC装到自己电脑上的虚拟机里面,然后管理小型机,但是在虚拟机里面使 ...
随机推荐
- Linux nohup命令详解
nohup命令及其输出文件 ...
- DB2JAVIT:RC=9505解决方案
DB2JAVIT:RC=9505解决方案 题记:WIN7下装DB2,启动任务中心.控制中心报DB2JAVIT:RC=9505. 解决方案:进入(计算机—>管理—>本地用户和组)把用户加入到 ...
- PHP_EOL常量
PHP_EOL 换行符 unix系列用 \n windows系列用 \r\n mac用 \r PHP中可以用PHP_EOL来替代,以提高代码的源代码级可移植性 如: <?php echo PHP ...
- CodeForces 554B(扫房间)
CodeForces 554B Time Limit:2000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u ...
- spoj SORTBIT - Sorted bit squence
Let's consider the 32 bit representation of all integers i from m up to n inclusive (m ≤ i ≤ n; m × ...
- OrCAD PSpice仿真库模型
说明:本介绍包含了\capture\library\pspice和capture\library\pspice\advanls目录下所有库,但由于作者水平有限,介绍得也比较简单,有些说明可能不一定对. ...
- 转:简单介绍 P3P 技术
原文来自于:http://blog.csdn.net/ghj1976/article/details/4889219 以 Internet Explorer 为例,默认情况下,IE的隐私策略如下图所设 ...
- hadoop 异常 INFO ipc.Client: Retrying connect to server:
// :: INFO ipc.Client: Retrying connect to server: master/. Already tried , sleepTime= SECONDS) // : ...
- GNU DAEMON THREAD <1>
尝试写一个简单的守护进程 /** @File daemon.c * * Build a daemon process for game * */ #include <unistd.h> # ...
- 【Java】详解Java解析XML的四种方法
XML现在已经成为一种通用的数据交换格式,平台的无关性使得很多场合都需要用到XML.本文将详细介绍用Java解析XML的四种方法. AD: XML现在已经成为一种通用的数据交换格式,它的平台无关性,语 ...