wsd 2014-08-28
最近用到了前台推送技术,一开始用了comet4j 但是发现偶尔会断开,所以探寻了另一条出路。mina+applet实现推送技术。
分为三步:一.编写mina服务端,客户端代码。(先调试好)
二.将客户端类变成applet类, 进行签章等设置
三.实现applet 和 jsp 通信
上代码,边看代码边将:
1.服务端类MinaServer
import java.io.IOException; import java.net.InetSocketAddress; import java.util.Collection; import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.transport.socket.SocketAcceptor; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; /** * 服务器启动类 * * @author JiangBo */ public class MinaServer { /** * 单例 */ private static MinaServer instance = new MinaServer(); public static MinaServer getInstance() { return instance; } private MinaServer() { } // 端口设置 private static int PORT = 30001; private SocketAcceptor acceptor; /** * 初始化方法 * * @return */ public boolean start() { // 创建非阻塞的server端的Socket连接 acceptor = new NioSocketAcceptor(); DefaultIoFilterChainBuilder filterChain = acceptor.getFilterChain(); // 添加编码过滤器 处理乱码、编码问题 // filterChain.addLast("codec", new ProtocolCodecFilter(new CharsetCodecFactory())); // 设置核心消息业务处理器 acceptor.setHandler(new ServerMessageHandler()); // 设置session配置,30秒内无操作进入空闲状态 acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); try { // 绑定端口 acceptor.bind(new InetSocketAddress(PORT)); } catch (IOException e) { e.printStackTrace(); return false; } return true; } /** * 向客户端发送(推送)消息 */ public void sendMessage(String host, String port, String value) { // 实例化一个向客户端发送的socket对象 Collection<IoSession> sessions = acceptor.getManagedSessions().values(); for (IoSession sess : sessions) { sess.write(value); } } /** * main方法 * * @param args */ public static void main(String[] args) { // MinaServer server = MinaServer.getInstance(); server.start(); System.out.println("服务器启动成功================="); } }
2.ServerMessageHandler类
import org.apache.mina.core.future.CloseFuture; import org.apache.mina.core.future.IoFuture; import org.apache.mina.core.future.IoFutureListener; import org.apache.mina.core.service.IoHandler; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; /** * 处理服务器端消息 * * @author JiangBo */ public class ServerMessageHandler implements IoHandler { /** * 服务器发生异常 */ @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { System.out.println("服务器发生异常: " + cause.getMessage()); } /** * 接收数据 */ @Override public void messageReceived(IoSession session, Object message) throws Exception { System.out.println("服务器接收到数据:" + message); } /** * 发送消息 */ @Override public void messageSent(IoSession session, Object message) throws Exception { System.out.println("服务器发送消息: " + message); } /** * 关闭链接 */ @Override public void sessionClosed(IoSession session) throws Exception { System.out.println("关闭当前session:" + session.getId() + session.getRemoteAddress()); CloseFuture closeFuture = session.close(true); closeFuture.addListener(new IoFutureListener<IoFuture>() { public void operationComplete(IoFuture future) { if (future instanceof CloseFuture) { ((CloseFuture) future).setClosed(); System.out.println("sessionClosed CloseFuture setClosed-->" + future.getSession().getId() + ","); } } }); } /** * 创建连接 */ @Override public void sessionCreated(IoSession session) throws Exception { System.out.println("创建一个新连接:" + session.getRemoteAddress()); session.write("session created:" + session.getRemoteAddress()); } /** * 空闲 */ @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { System.out.println("当前连接" + session.getRemoteAddress() + "处于空闲状态:" + status); } /** * 打开一个session链接 */ @Override public void sessionOpened(IoSession session) throws Exception { System.out.println("打开一个session:"+ session.getId() + session.getBothIdleCount()); } }
3.MinaClient客户端类
import java.applet.Applet; import java.net.InetSocketAddress; import netscape.javascript.JSObject; import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder; import org.apache.mina.core.future.CloseFuture; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.transport.socket.SocketConnector; import org.apache.mina.transport.socket.nio.NioSocketConnector; import com.txsoft.ipcc.common.mina.service.CharsetCodecFactory; /** * mina客户端 * * @author JiangBo */ public class MinaClient extends Applet { /** * serialVersionUID */ private static final long serialVersionUID = 1L; private static String IP = "192.168.1.30"; private static int PORT = 30001; private SocketConnector connector; private ConnectFuture future; private IoSession session; public void init() { JSObject window= null; try { window=JSObject.getWindow(this); window.eval("alert('start begin!')"); } catch (Exception e) { } // 创建一个socket连接 connector = new NioSocketConnector(); // 设置链接超时时间 connector.setConnectTimeoutMillis(3000); // 获取过滤器链 DefaultIoFilterChainBuilder filterChain = connector.getFilterChain(); // 添加编码过滤器 处理乱码、编码问题 filterChain.addLast("codec", new ProtocolCodecFilter(new CharsetCodecFactory())); // 消息核心处理器 connector.setHandler(new ClientMessageHandlerAdapter(this)); // 连接服务器,知道端口、地址 future = connector.connect(new InetSocketAddress(IP, PORT)); // 等待连接创建完成 future.awaitUninterruptibly(); // 获取当前session session = future.getSession(); // try { window.eval("alert('end!')"); } catch (Exception e) { } } public void setAttribute(Object key, Object value) { session.setAttribute(key, value); } /** * 发送 * @param message */ public void send(String message) { session.write(message); } /** * 发送信息到JSP * @param message */ public void sendJsp(String message) { // 获取JavaScript窗口句柄,引用当前文档窗口 JSObject window = JSObject.getWindow(MinaClient.this); // 调用页面上的js方法 show(message) Object obj[] = new Object[1]; obj[0] = message; window.call("show", obj);// 参数用数组的形势表示。 } public boolean close() { CloseFuture future = session.getCloseFuture(); future.awaitUninterruptibly(1000); connector.dispose(); return true; } public SocketConnector getConnector() { return connector; } public IoSession getSession() { return session; } }
4.ClientMessageHandlerAdapter类
import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IoSession; /** * 客户端消息处理类 * @author JiangBo */ public class ClientMessageHandlerAdapter extends IoHandlerAdapter { private MinaClient minaClient = null; public ClientMessageHandlerAdapter(MinaClient minaClient) { this.minaClient = minaClient; } /** * 接收到消息 */ public void messageReceived(IoSession session, Object message) throws Exception { // 接到消息无处理 System.out.println("messageSent服务端发送到客户端的消息:" + message); if (null != this.minaClient) { try { this.minaClient.sendJsp(message.toString()); } catch (Exception e) { } } } public void messageSent(IoSession session , Object message) throws Exception{ System.out.println("messageSent 客户端发送消息:" + message); } @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { System.out.println("服务器发生异常: {}" + cause.getMessage()); } }
5. jsp文件
<%@ page contentType="text/html; charset=utf-8"%> <html> <head> <title> test mina push </title> </head> <body> <applet name = "testapplet" code = 'com.txsoft.ipcc.common.mina.client.MinaClient.class' archive = 'minaClient.jar' width = 100 height = 50 MAYSCRIPT> </applet> <script type="text/javascript"> function show(message){ alert(message); } </script> </body> </html>
注意:例子中要把代码打包放在跟目录下,例子中jar包名叫:minaClient.jar。jar包还需要密码的支持所以打包时候还要把mima的支持包打进去。把下面3个包放在跟minaClient.jar同级目录下。
1.mina-core-2.0.7.jar
2.slf4j-api-1.7.6.jar
3.slf4j-jdk14-1.7.7.jar
打包时候我是通过MANIFEST.MF文件选择了密码支持的3个包
MANIFEST.MF内容:
Manifest-Version: 1.0 Class-Path: mina-core-2.0.7.jar slf4j-api-1.7.6.jar slf4j-jdk14-1.7.7.jar
由于是Applet所以需要生成证书及签名所以在服务端cmd调出命令框 执行授权:
1、keytool -genkey -keystore pepper.store -alias pepper
这个命令用来产生一个密匙库,执行完毕后应该在c:/admin中产生一个pepper.store的文件,这里的pepper是我自己的名字,你可以对它进行修改。另外在执行命令的时候还有提示你输入密匙库的密码,这里你一定要记住,否则后面要用的时候无法输入。
2、keytool -export -keystore pepper.store -alias pepper -file pepper.cert
这个命令用来产生签名时所要用的证书,同样这里的pepper也可以换成你自己需要的名字。这个命令执行完后在c:/admin中产生一个pepper.cert的文件。
3、 jarsigner -keystore pepper.store E:\workspace_IPCC\IPCC\WebRoot\minaClient.jar pepper
这个命令用上面产生的证书将我们的jar文件进行了签名。(可以写绝对路径)
在客户端设置security
C:\Program Files (x86)\Java\jre6\lib\security\java.policy
在最下面添加这句,是允许所有IP 所有端口的权限,让然你可以可以指定IP 和端口
permission java.net.SocketPermission "*:*","accept,connect,resolve";
好了 小伙伴们 快去试试吧~
需要的jar包: