mina整合spring ,服务端反向发送消息到客户端 完整实例
来源:互联网 发布:python将字典传入函数 编辑:程序博客网 时间:2024/05/17 03:11
之前的项目需要用到mina,实现的功能主要是:服务端主动发送消息到客户端,这个的服务端为外网的tomcat,客户端为内网的tomcat,由于无法知道内网tomcat 的地址,也就不能直接通过http的方式发送信息回来,最后想来想去用mina实现了这个功能。
当然,我这里的服务端是整合的了spring 的,也可以直接把服务端独立出来,不整合spring,这个都一样,区别不大。
代码和配置如下:
---------------------------
1,jar包,我这里使用的是spring4.0.5,mina2.0.7
maven部分文件如下,这个包会自动也依赖进来mina-filter-ssl-1.1.7.jar
<!-- spring 相关 -->
<dependency><span style="white-space:pre"></span><groupId>org.springframework</groupId><span style="white-space:pre"></span><artifactId>spring-context-support</artifactId><span style="white-space:pre"></span><version>${springframework-version}</version><span style="white-space:pre"></span></dependency><span style="white-space:pre"></span><dependency><span style="white-space:pre"></span><groupId>org.springframework</groupId><span style="white-space:pre"></span><artifactId>spring-jdbc</artifactId><span style="white-space:pre"></span><version>${springframework-version}</version><span style="white-space:pre"></span></dependency><span style="white-space:pre"></span><dependency><span style="white-space:pre"></span><groupId>org.springframework</groupId><span style="white-space:pre"></span><artifactId>spring-orm</artifactId><span style="white-space:pre"></span><version>${springframework-version}</version><span style="white-space:pre"></span></dependency><span style="white-space:pre"></span><dependency><span style="white-space:pre"></span><groupId>org.springframework</groupId><span style="white-space:pre"></span><artifactId>spring-aop</artifactId><span style="white-space:pre"></span><version>${springframework-version}</version><span style="white-space:pre"></span></dependency><span style="white-space:pre"></span><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>${springframework-version}</version><exclusions><exclusion><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${springframework-version}</version></dependency><!-- Aspectj --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>${aspectj-version}</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>${aspectj-version}</version></dependency><!-- mina --><dependency><groupId>org.apache.mina</groupId><artifactId>mina-core</artifactId><version>2.0.7</version></dependency><dependency><groupId>org.apache.mina</groupId><artifactId>mina-integration-spring</artifactId><version>1.1.7</version></dependency><dependency><groupId>org.apache.mina</groupId><artifactId>mina-integration-beans</artifactId><version>2.0.8</version></dependency>
2,spring-ztc_app-mina.xml (spring与mina 的配置文件)
spring与mina 的配置文件,需要导入到spring 的总配置文件中,或者加入到web.xml的spring监听扫描中
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"><!-- 配置地址 --><bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"><property name="customEditors"><map><!-- spring升级后此配置已失效 会报错<entry key="java.net.SocketAddress"><bean class="org.apache.mina.integration.beans.InetSocketAddressEditor" /></entry> --><!-- 修改这里 --><entry key="java.net.SocketAddress"value="org.apache.mina.integration.beans.InetSocketAddressEditor"></entry></map></property></bean><!-- 配置业务处理类 --><bean id="serviceHandler" class="cn.hydom.ztc.ztc_app.controller.mina.ServerHandler" /><!-- 配置service --><bean id="ioAcceptor" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor"init-method="bind" destroy-method="unbind"><property name="defaultLocalAddress" value=":6007" /><property name="handler" ref="serviceHandler" /><property name="filterChainBuilder" ref="filterChainBuilder" /></bean><!-- 配置解码器 --><bean id="codec" class="org.apache.mina.filter.codec.ProtocolCodecFilter"><constructor-arg><!-- <bean class="org.apache.mina.filter.codec.textline.TextLineCodecFactory" /> --><!-- 自定义的 字符编码类 --><bean class="cn.hydom.ztc.ztc_app.controller.mina.HCoderFactory" /></constructor-arg></bean><!-- 配置日志拦截器 --><bean id="logger" class="org.apache.mina.filter.logging.LoggingFilter"></bean><!-- 将日志和解码器添加 --><bean id="filterChainBuilder"class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder"><property name="filters"><map><entry key="codec" value-ref="codec" /><entry key="logger" value-ref="logger" /></map></property></bean></beans>
3,mina服务端业务处理类
package cn.hydom.ztc.ztc_app.controller.mina;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.mina.core.service.IoHandlerAdapter;import org.apache.mina.core.session.IdleStatus;import org.apache.mina.core.session.IoSession;/** * @Description: mina服务端业务处理类 * @author whl * @date 2014-9-30 下午12:36:28 * */public class ServerHandler extends IoHandlerAdapter {private final static Log log = LogFactory.getLog(ServerHandler.class); public ServerHandler() { // TODO Auto-generated constructor stub } @Override public void exceptionCaught(IoSession session, Throwable cause) throws Exception { } @Override public void messageReceived(IoSession session, Object message) throws Exception { log.debug("服务端收到信息-------------"); //获取客户端发过来的key String key = message.toString(); System.out.println("message :"+message.toString()); String carPark_id = key.substring(key.indexOf("=") + 1); System.out.println("carPark_id :"+carPark_id); //保存客户端的会话session SessionMap sessionMap = SessionMap.newInstance(); sessionMap.addSession(carPark_id, session); } @Override public void messageSent(IoSession session, Object message) throws Exception { log.debug("------------服务端发消息到客户端---"); } @Override public void sessionClosed(IoSession session) throws Exception { // TODO Auto-generated method stub log.debug("远程session关闭了一个..." + session.getRemoteAddress().toString()); } @Override public void sessionCreated(IoSession session) throws Exception { log.debug(session.getRemoteAddress().toString() +"----------------------create"); } @Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { log.debug(session.getServiceAddress() +"IDS"); } @Override public void sessionOpened(IoSession session) throws Exception { log.debug("连接打开:"+session.getLocalAddress()); } }
package cn.hydom.ztc.ztc_app.controller.mina;import java.util.HashMap;import java.util.Map;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.mina.core.session.IoSession;/** * @Description: 单例工具类,保存所有mina客户端连接 * @author whl * @date 2014-9-29 上午10:09:15 * */public class SessionMap {private final static Log log = LogFactory.getLog(SessionMap.class);private static SessionMap sessionMap = null;private Map<String, IoSession>map = new HashMap<String, IoSession>();//构造私有化 单例private SessionMap(){}/** * @Description: 获取唯一实例 * @author whl * @date 2014-9-29 下午1:29:33 */public static SessionMap newInstance(){log.debug("SessionMap单例获取---");if(sessionMap == null){sessionMap = new SessionMap();}return sessionMap;}/** * @Description: 保存session会话 * @author whl * @date 2014-9-29 下午1:31:05 */public void addSession(String key, IoSession session){log.debug("保存会话到SessionMap单例---key=" + key);this.map.put(key, session);}/** * @Description: 根据key查找缓存的session * @author whl * @date 2014-9-29 下午1:31:55 */public IoSession getSession(String key){log.debug("获取会话从SessionMap单例---key=" + key);return this.map.get(key);}/** * @Description: 发送消息到客户端 * @author whl * @date 2014-9-29 下午1:57:51 */public void sendMessage(String[] keys, Object message){for(String key : keys){IoSession session = getSession(key);log.debug("反向发送消息到客户端Session---key=" + key + "----------消息=" + message);if(session == null){return;}session.write(message);}}}
5,编码解码器,
HCoderFactory.java
package cn.hydom.ztc.ztc_app.controller.mina;import java.nio.charset.Charset;import org.apache.mina.core.session.IoSession;import org.apache.mina.filter.codec.ProtocolCodecFactory;import org.apache.mina.filter.codec.ProtocolDecoder;import org.apache.mina.filter.codec.ProtocolEncoder;/** * @Description: 编码和解码器工厂类. * @author whl * @date 2014-9-30 下午12:34:59 * */public class HCoderFactory implements ProtocolCodecFactory {private final HEncoder encoder;private final HDecoder decoder;public HCoderFactory() {//this(Charset.defaultCharset());this(Charset.forName("UTF-8"));}public HCoderFactory(Charset charSet) {this.encoder = new HEncoder(charSet);this.decoder = new HDecoder(charSet);}@Overridepublic ProtocolDecoder getDecoder(IoSession arg0) throws Exception {return decoder;}@Overridepublic ProtocolEncoder getEncoder(IoSession arg0) throws Exception {return encoder;}}
HDecoder.java
package cn.hydom.ztc.ztc_app.controller.mina;import java.nio.charset.Charset;import java.nio.charset.CharsetDecoder;import org.apache.mina.core.buffer.IoBuffer;import org.apache.mina.core.session.IoSession;import org.apache.mina.filter.codec.CumulativeProtocolDecoder;import org.apache.mina.filter.codec.ProtocolDecoderOutput;/** * @Description: 解码工具类 * @author whl * @date 2014-9-30 下午12:35:22 * */public class HDecoder extends CumulativeProtocolDecoder {private final Charset charset;public HDecoder(Charset charset) {this.charset = charset;}public boolean doDecode(IoSession session, IoBuffer in,ProtocolDecoderOutput out) throws Exception {//System.out.println("-------doDecode----------");CharsetDecoder cd = charset.newDecoder();String mes = in.getString(cd);out.write(mes);return true;/*if (in.remaining() > 4) {// 有数据时,读取字节判断消息长度in.mark();// 标记当前位置,以便resetint size = in.getInt();// 如果消息内容不够,则重置,相当于不读取sizeif (size > in.remaining()) {in.reset();return false;// 接收新数据,以拼凑成完整数据} else if (size != 0 && (size - 4 >= 0)) {byte[] bytes = new byte[size - 4];//int protocol = in.getInt();// 拿到客户端发过来的数据组装成基础包写出去in.get(bytes, 0, size - 4);//in.get(bytes, size - 4, size);PackageBeanFactory beanFactory = (PackageBeanFactory) session.getAttribute(ServerHandler.BEAN_FACTORY);//out.write(beanFactory.getPackage(protocol, size, bytes));String mes = in.getString(cd);out.write(mes);// 如果读取内容后还粘了包,就让父类再给读取进行下次解析if (in.remaining() > 0) {return true;}}}return false;// 处理成功,让父类进行接收下个包*/}}
HEncoder.java
package cn.hydom.ztc.ztc_app.controller.mina;import java.nio.charset.Charset;import java.nio.charset.CharsetDecoder;import java.nio.charset.CharsetEncoder;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.mina.core.buffer.IoBuffer;import org.apache.mina.core.session.IoSession;import org.apache.mina.filter.codec.ProtocolEncoder;import org.apache.mina.filter.codec.ProtocolEncoderOutput;/** * @Description: 编码工具类 * @author whl * @date 2014-9-30 下午12:35:35 * */public class HEncoder implements ProtocolEncoder {private final static Log log = LogFactory.getLog(HEncoder.class);private final Charset charset;public HEncoder(Charset charset) {this.charset = charset;}@Overridepublic void encode(IoSession session, Object message,ProtocolEncoderOutput out) throws Exception {CharsetEncoder ce = charset.newEncoder();String mes = (String) message;IoBuffer buffer = IoBuffer.allocate(100).setAutoExpand(true); buffer.putString(mes,ce); buffer.flip(); out.write(buffer);/*System.out.println("---------encode-------------");String mes = (String) message;byte[] data = mes.getBytes("UTF-8");IoBuffer buffer = IoBuffer.allocate(data.length + 4);buffer.putInt(data.length);buffer.put(data);buffer.flip();out.write(buffer);out.flush();*/}@Overridepublic void dispose(IoSession session) throws Exception {log.info("Dispose called,session is " + session);}}
6,客户端程序
ClentMain.java
package cn.hydom.ztc.ztc_app.controller.mina;import java.net.InetSocketAddress;import java.text.SimpleDateFormat;import java.util.Date;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.mina.core.filterchain.IoFilterAdapter;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.filter.logging.LoggingFilter;import org.apache.mina.transport.socket.nio.NioSocketConnector;/** * @Description: mina客户端,包含断线重连机制,空闲重连机制 * @author whl * @date 2014-11-2 */public class ClentMain extends Thread{private final static Log log = LogFactory.getLog(ClentMain.class);@Overridepublic void run() {//ipString host = "192.168.0.38";//端口int port = 6007;//停车场idfinal String carPark_id = "1";// 创建客户端连接器.final NioSocketConnector connector = new NioSocketConnector();//设置连接超时 connector.setConnectTimeoutMillis(30000); // 设置默认访问地址 connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));//将IoSession的主键属性注入线程映射表MDC中 //connector.getFilterChain().addLast("mdc", new MdcInjectionFilter()); //日志过滤器connector.getFilterChain().addLast("logger", new LoggingFilter());// 设置编码过滤器connector.getFilterChain().addLast("codec",new ProtocolCodecFilter(new HCoderFactory())); //添加处理器 connector.setHandler(new ClintHandler()); // 设置接收缓冲区的大小 connector.getSessionConfig().setReceiveBufferSize(10240); // 设置输出缓冲区的大小 connector.getSessionConfig().setSendBufferSize(10240); /** * 空闲重连的机制,根据需要选择相应的配置 */ // 读写都空闲时间:30秒 //connector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); // 读(接收通道)空闲时间:40秒 //connector.getSessionConfig().setIdleTime(IdleStatus.READER_IDLE, 40); // 写(发送通道)空闲时间:50秒 //connector.getSessionConfig().setIdleTime(IdleStatus.WRITER_IDLE, 50); //断线重连回调拦截器 connector.getFilterChain().addFirst("reconnection", new IoFilterAdapter() { @Override public void sessionClosed(NextFilter nextFilter, IoSession ioSession) throws Exception { for(;;){ try{ Thread.sleep(3000); ConnectFuture future = connector.connect(); future.awaitUninterruptibly();// 等待连接创建成功 IoSession session = future.getSession();// 获取会话 session.write("key="+carPark_id); if(session.isConnected()){ log.info("断线重连["+ connector.getDefaultRemoteAddress().getHostName() +":"+ connector.getDefaultRemoteAddress().getPort()+"]成功"); //System.out.println("断线重连["+ connector.getDefaultRemoteAddress().getHostName() +":"+ connector.getDefaultRemoteAddress().getPort()+"]成功"); break; } }catch(Exception ex){ log.info("重连服务器登录失败,3秒再连接一次:" + ex.getMessage()); //System.out.println("重连服务器登录失败,3秒再连接一次:" + ex.getMessage()); } } } }); //开始连接 for (;;) { try { ConnectFuture future = connector.connect(); // 等待连接创建成功 future.awaitUninterruptibly(); // 获取会话 IoSession session = future.getSession(); //发送消息 session.write("key=" + carPark_id); log.error("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())); break; } catch (Exception e) { //System.out.println("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage()); log.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e); // 连接失败后,重连10次,间隔30s try {Thread.sleep(5000);} catch (InterruptedException e1) {e1.printStackTrace();log.error("连接服务端失败后,睡眠5秒发生异常!");} } } // cf.getSession().write("quit");//发送消息//cf.getSession().close();//cf.getSession().getCloseFuture().awaitUninterruptibly();// 等待连接断开//connector.dispose();}}
ClintHandler.java
package cn.hydom.ztc.ztc_app.controller.mina;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.apache.mina.core.service.IoHandlerAdapter;import org.apache.mina.core.session.IdleStatus;import org.apache.mina.core.session.IoSession;/** * @Description: 客户端业务处理类 * @author whl * @date 2014-11-2 */public class ClintHandler extends IoHandlerAdapter {private final static Log log = LogFactory.getLog(ClintHandler.class);/** * 写处理服务端推送的信息的逻辑 */@Overridepublic void messageReceived(IoSession session, Object message)throws Exception {System.out.println("-----服务顿返回的json数据----");String s = message.toString();System.out.println("message :" + s);System.out.println("message length:" + s.length());}@Override public void sessionIdle(IoSession session, IdleStatus status) throws Exception { log.info("-客户端与服务端连接[空闲] - " + status.toString()); System.out.println("-客户端与服务端连接[空闲] - " + status.toString()); if(session != null){ session.close(true); } } }
7,测试
这里就不写实际的代码了,
1,首先部署web工程启动tomcat,让服务端先运行起来,
2,然后运行客户端
public class ClintTest1 {public static void main(String[] args) throws Exception {ClentMain mina = new ClentMain();mina.start();}}
3,想要服务端主动发回来信息,还得在服务端的web工程的action中写一个htpp访问的方法,方法中去sessionMap类中缓存的map中查找session,然后发送消息。
代码如下,我用的是springmvc
/** * @Description: 发送消息到客户端 * @author whl * @date 2014-9-29 下午1:18:54 */@ResponseBody@RequestMapping(value="/sendMessage")public String <span style="font-family: Arial, Helvetica, sans-serif;">sendMessage</span><span style="font-family: Arial, Helvetica, sans-serif;">(HttpServletRequest request, String[] carPark_id){</span>try{////获取链接的参数<span style="font-family: Arial, Helvetica, sans-serif;">carPark_id</span>log.debug("carPark_id[].length--------- " + carPark_id.length);for(String id : carPark_id){log.debug("carPark_id --------- " + id);}//这里用的假数据<pre name="code" class="java"><span style="font-family: Arial, Helvetica, sans-serif;"> carPark_id = new String[]{"1"};</span>//发送的信息String jsonstr = "123";//反向发送信息log.debug("开始反向发送消息到客户端-------- ");SessionMap sessionMap = SessionMap.newInstance();sessionMap.sendMessage(carPark_id, jsonstr);//返回信息return "发送的信息为" +jsonstr;
}catch (Exception e) {log.error(e);return "出错了。。";}}
4,好了,现在重新发布工程,启动服务端和客户端,然后访问这个链接就可以了,当然springmvc需要自己配置,这也不是这里的重点。
注意:客户端发过来的carPark_id必须与服务端查找的一致,不让就找不到相应的客户端session连接,消息无法发送成功。
- mina整合spring ,服务端反向发送消息到客户端 完整实例
- Mina框架实现客户端与服务端实相互发送消息
- java客户端发送消息到服务端
- MINA客户端与服务端通信实例
- mina服务端检测客户端心跳实例
- spring整合cxf 客户端+服务端
- spring整合activemq发送MQ消息[Topic模式]实例,activemqmq
- spring整合activemq发送消息[queue类型]实例
- spring整合activemq发送消息[queue类型]实例
- spring mina整合(客户端进行调用)
- 客户端IdTCPClient向服务端IdTCPServer1发送消息
- SSL客户端向服务端发送消息
- 客户端往服务端定时发送消息
- Spring整合Apache CXF(服务端+客户端)
- java客户端发消息到服务端
- Android 用socket实现客户端发送消息,服务端显示消息
- Mina TCP服务端客户端 示例
- Mina TCP服务端客户端 示例
- 《Accelerate C++》读书笔记(一)
- Linux下Verilog仿真过程(二)
- Java IO 目录文件复制与删除以及目录大小与文件个数统计
- 手把手教你用NDK9编译ffmpeg2.4.2
- hdu - 2669 - Romantic(扩展欧几里得)
- mina整合spring ,服务端反向发送消息到客户端 完整实例
- 十月学习总结
- HDU 2136 Largest prime factor 参考代码
- 听姚明的,鼓励身体对抗!
- [uva 11916]Emoogle Grid 数学 BSGS
- 寻找数组最大K个数
- Revenge of LIS II (hdu 5087 LIS)
- VS使用过程中的常用技巧
- onBlur事件与onfocus事件(js)