https://github.com/tylermenezes/SerialServe

https://github.com/straend/SerialWebsocket

http://www.cnblogs.com/lcchuguo/p/4007392.html

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

web应用程序访问串口的更多相关文章

  1. 您试图在此 Web 服务器上访问的 Web 应用程序当前不可用

    错误提示: 服务器应用程序不可用 您试图在此 Web 服务器上访问的 Web 应用程序当前不可用.请点击 Web 浏览器中的“刷新”按钮重试您的请求. 管理员注意事项: 详述此特定请求失败原因的错误信 ...

  2. 问题-关于sharemem程序访问WEB出现内存错误处理

    [delphi技术] 关于sharemem造成dll错误的处理办法问题现象:如果程序和dll之间用string作为参数传递时容易出现错误问题处理:需要在程序的uses中使用sharemem.这个sha ...

  3. Web应用程序项目XXXX已配置为使用IIS。无法访问IIS元数据库。您没有足够的特权访问计算机上的IIS网站

    问题:Windows8下直接使用VS打开项目,出现问题:XXXX已配置为使用IIS.无法访问IIS元数据库.您没有足够的特权访问计算机上的IIS网站.解决:1.以“管理员权限”运行VS,在VS菜单打开 ...

  4. Web 应用程序项目 XXXX 已配置为使用 IIS。 无法访问 IIS 元数据库。您没有足够的特权访问计算机上的 IIS 网站。(转载)

    Web 应用程序项目 XXXX 已配置为使用 IIS. 无法访问 IIS 元数据库.您没有足够的特权访问计算机上的 IIS 网站. 2012年05月19日 ⁄ 综合 ⁄ 共 261字 ⁄ 字号 小 中 ...

  5. Web 应用程序项目 Himall.Web 已配置为使用 IIS。 无法访问 IIS 元数据库

    Web应用程序项目XXXX已配置为使用IIS.无法访问IIS元数据库.您没有足够的特权访问计算机上的IIS网站,xxxxiis 问题:Windows8下直接使用VS打开项目,出现问题:XXXX已配置为 ...

  6. Web应用程序项目XXXX已配置为使用IIS。无法访问IIS 元数据库。您没有足够的特权访问计算机上的IIS

    错误图片:

  7. 部署Azure环境Web应用程序不能直接访问JSON文件解决方案

    问题: 部署在Azure环境Web应用程序的JSON文件,直接通过浏览器或Web应用访问出现 404 的错误信息. 以下通过Firfox浏览器直接访问JSON文件返回的提示错误信息: “HTML 文档 ...

  8. vs2012 发布web应用程序

    Visual Studio 2012 Visual Studio Express 2012 for Web 与 的Visual Studio 2010  Visual Studio Web发布更新 与 ...

  9. web应用程序

    1.web应用程序和网站的区别 应用程序有两种模式C/S.B/S.C/S是客户端/服务器端程序,也就是说这类程序一般独立运行.而B/S就是浏览器端/服务器端应用程序,这类应用程序一般借助IE等浏览器来 ...

随机推荐

  1. python—第三库的安装方法

    Windows系统下安装第三方Python库的三种方法: 1.使用easy_install命令安装 一般在安装完Python后再C:\Python27\Scripts 目录下有 easy_instal ...

  2. Python— isinstance用法说明

    在学习自动化测试的脚本中发现了这个函数,所以在网上查了一下资料进行如下整理: 通过帮助查看如下: 作用:来判断一个对象是否是一个已知的类型: 其第一个参数(object)为对象,第二个参数为类型名(i ...

  3. WordPress 客户端软件列表

    Windows: BlogDesk BlogJet Blog Writer Chrysanth WebStory Deepest Sender (Firefox或SeaMonkey扩展,跨平台- De ...

  4. MVC把表格导出到Excel

    有关Model: namespace MvcApplication1.Models { public class Coach { public int Id { get; set; } public ...

  5. Eclipse批量替换

    情景: 我需要将项目中所有有"上样板"的字样替换为"PCR板",如果寻找单个页面肯定是很麻烦,而且替换很有可能不全,那么该怎么才能完全替换呢? 解决方法: ec ...

  6. Android图片加载框架最全解析(六),探究Glide的自定义模块功能

    不知不觉中,我们的Glide系列教程已经到了第六篇了,距离第一篇Glide的基本用法发布已经过去了半年的时间.在这半年中,我们通过用法讲解和源码分析配合学习的方式,将Glide的方方面面都研究了个遍, ...

  7. 借助 Resharper 和 StyleCop 让代码更整洁

    一:工具安装 Resharper 和 StyleCop 必须安装. Resharper 的配置文件如下:Resharper.zip 请按如下步骤导入, 1: 2: 3:   StyleCope 的配置 ...

  8. 信号处理篇alarm ferror kill mkfifo pause pclose perror pipe popen sigaction sigaddset sigdelset sigemptyset signal sleep strerror

    alarm(设置信号传送闹钟) 相关函数 signal,sleep 表头文件 #include<unistd.h> 定义函数 unsigned int alarm(unsigned int ...

  9. Flask 学习(二)路由

    Flask  路由 在说明什么是 Flask 路由之前,详细阐述下 Flask “Hello World” 这一 最小应用的代码. Flask “Hello World” from flask imp ...

  10. 用java在客户端读取mongodb中的数据并发送至服务器

    使用Java自带的socket端口来实现,程序如下: Client.java package com.cn.gao; import java.net.*; import java.io.*; impo ...