mina相关应用

结合http://blog.csdn.net/wuliusir/article/details/51206654这篇博客来了mina在项目中的用法


用配置文件来管理

-----------------------------------------------------------------------------

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://192.168.32.98:8083/spring-beans.dtd">
<beans>
    <!-- This makes it possible to specify java.net.SocketAddress values (e.g.
        :80 below) as Strings. They will be converted into java.net.InetSocketAddress
        objects by Spring. -->
    <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
        <property name="customEditors">
            <map>
                <entry key="java.net.SocketAddress">
                    <bean class="org.apache.mina.integration.beans.InetSocketAddressEditor" />
                </entry>
            </map>
        </property>
    </bean>

    <!-- The IoHandler implementation -->
    <bean id="mhHandler" class="com.cvicse.ksccs.fes.socketserver.SocketServerHandler">
    </bean>

    <bean id="executorFilter" class="org.apache.mina.filter.executor.ExecutorFilter">
        <constructor-arg value="10" />
        <constructor-arg value="50" />
    </bean>

    <bean id="codecFilter" class="org.apache.mina.filter.codec.ProtocolCodecFilter">
        <constructor-arg>
            <bean class="com.cvicse.ksccs.fes.socketserver.IntMsgLenghtProtocolCodecFactory" />
        </constructor-arg>
    </bean>

    <bean id="loggingFilter" class="org.apache.mina.filter.logging.LoggingFilter" />

    <bean id="filterChainBuilder"
        class="org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder">
        <property name="filters">
            <map>
                <entry key="executor" value-ref="executorFilter" />
                <entry key="codecFilter" value-ref="codecFilter" />
                <entry key="loggingFilter" value-ref="loggingFilter" />
            </map>
        </property>
    </bean>

    <bean id="ioAcceptor" class="org.apache.mina.transport.socket.nio.NioSocketAcceptor"
        init-method="bind" destroy-method="unbind">
        <property name="defaultLocalAddress" value="9988" />
        <property name="handler" ref="mhHandler" />
        <property name="reuseAddress" value="true" />
        <property name="filterChainBuilder" ref="filterChainBuilder" />
    </bean>

</beans>
---------------------------------------------------------------------------------------------------------------------



Server 端的 Main 函数:

   

  IoAcceptor acceptor = new NioSocketAcceptor(); // 建立监控器

//acceptor.getSessionConfig().setReadBufferSize(2048);

//acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE , 10);

acceptor.getFilterChain().addLast("codec ",

      New ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"),

LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue())));// 加载解 / 编码工厂

acceptor.setHandler(new StandBaowenHandler ()); // 设置处理类,其主要内容见下。

acceptor.bind(new InetSocketAddress(9998));// 绑定的监听端口,可多次绑定,也可同时绑定多个。

 

StandBaowenHandler 的关键代码

public void messageReceived(IoSession session, Object message) throws Exception {          session.getService().getManagedSessions();

              String baowenSrc = message.toString();// 原始报文

              System.out.println (baowenSrc );

       }



(1) IoService :这个接口在一个线程上负责套接字的建立,拥有自己的 Selector ,监听是否有连接被建立。

(2) IoProcessor :这个接口在另一个线程上负责检查是否有数据在通道上读写,也就是说它也拥有自己的 Selector ,这是与我们使用 JAVA NIO 编码时的一个不同之处,

通常在 JAVA NIO 编码中,我们都是使用一个 Selector ,也就是不区分 IoService 与 IoProcessor 两个功能接口。另外,IoProcessor 也是 MINA 框架的核心组件之一 . 在 MINA 框架启动时,会用一个线程池来专门生成线程,来负责调用注册在IoService 上的过滤器,并在过滤器链之后调用 IoHandler 。在默认情况 IoProcessor 会用 N+1 个线程来轮流询问监视的端口是否有数据传送,其中 n 为 cpu 的内核个数。按一般的多线程设计概念来说, IoProcessor 的线程数是越多越好,但实际上并非如此,因为大家都知道, IO 的操作是非常占用资源的,所以项目中的 IoProcessor 的线程数应该根据实际需要来定,而这个数字可以在生成 IoAcceptor 对象时进行设定。 Eg IoAcceptor acceptor = new NioSocketAcceptor( N );

 

(3.) IoFilter :这个接口定义一组拦截器,这些拦截器可以包括日志输出、黑名单过滤 ( 参见之前代码示例的注释部份 ) ,甚至是在过滤器链中利用 AOP 写上权限控制(笔者负责的部分没有涉及到这权限部分,但根据笔者对框架的理解要实现这一点应该问题不大,有需要的可以自行试验)。数据的编码( write 方向)与解码( read 方向)等功能,其中数据的 encode 与 decode是最为重要的、也是您在使用 Mina 时最主要关注的地方(笔者曾经为了 decode 的解码编写,重写了不下十几种实现方式才找到准确无误适合项目的解码方法)。

(4.) IoHandler :这个接口负责编写业务逻辑,也就是接收、发送数据的地方。只本文的代码实例中,可以看到真正的业务代码只有一句 :System.out.println(str); 真实项目中当然不可能如此简单,但如果大家把业务处理写好,并写好业务接口,真正要用时,呆需要在此处替换即可,再次见证了 MINA 分层的彻底。


对应的代码:


------------------------------------------------------handler----------------------------------------------------------------------

package com.cvicse.ksccs.fes.socketserver;

import java.util.HashMap;

import java.util.Map;
import org.apache.log4j.Logger;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.transport.socket.SocketSessionConfig;
import com.cvicse.eps.common.Constant;
import com.cvicse.eps.common.ResponseType;
import com.cvicse.ksccs.fes.business.ReqMsgHandlerItf;
import com.cvicse.ksccs.fes.business.impl.PMSReqMsgHandler;
import com.cvicse.ksccs.fes.config.FESDealConfig;
import com.cvicse.ksccs.fes.config.FESSocketConfig;
import com.cvicse.ksccs.fes.jms.SocketClients;
import com.cvicse.ksccs.fes.localresource.LocalResource;
import com.cvicse.ksccs.fes.message.FESMessage;
import com.cvicse.ksccs.fes.message.MessageDeal;
import com.cvicse.ksccs.fes.message.MessageHeader;
import com.cvicse.ksccs.fes.message.ParserDealFactory;
import com.cvicse.ksccs.fes.util.FESConstant;
import com.cvicse.ksccs.fes.util.MsgMAC;

/**
 *
 * @description mina-socket处理类
 * @author fei_yfan
 * @version 1.0
 * @create_date 2013-10-17
 * @copyright (c) CVIC SE
 */
public class SocketServerHandler extends IoHandlerAdapter {

    public static Logger logger = Logger.getLogger(SocketServerHandler.class);

    /**
     * 当会话创建是调用该方法
     */
    public void sessionCreated(IoSession session) throws Exception {
        logger.info("服务端与客户端" + session.getRemoteAddress() + "创建连接...");
        SocketSessionConfig cfg = (SocketSessionConfig) session.getConfig();
        cfg.setKeepAlive(true);
        // cfg.setSoLinger(0); // 这个是根本解决问题的
    }

    /**
     * 当网络连接被打开时此方法被调用,这里可以对session设置一些参数或者添加一些IoFilter的实现,也可以对客户端做一些认证之类的工作
     */
    public void sessionOpened(IoSession session) throws Exception {
        logger.info("服务端与客户端" + session.getRemoteAddress() + "连接打开...");
        session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
    }

    /**
     * 接收消息函数
     */
    public synchronized void messageReceived(IoSession session, Object message) {

        String responseMsg = "";
        MessageHeader reqMsgHeader = new MessageHeader();
        Map<String, String> reqMsgBody = new HashMap<String, String>();

        IoBuffer messageIo = (IoBuffer) message;
        IoBuffer buf = null;
        // 缴费平台服务IP
        String epsIP= FESSocketConfig.getFESConfig().getLtEpsIp();
        // 清算服务IP
        String bepsIP= FESSocketConfig.getFESConfig().getLtBepsIp();
        try {

            byte[] bytes = messageIo.array();

            String tempMsg = "";

            tempMsg = new String(bytes);

            logger.info("\n监听到的请求报文:" + tempMsg);
            // 区分清算报文、水报文
            String IP=session.getRemoteAddress().toString();
            IP=IP.substring(IP.indexOf("/")+1,IP.indexOf(":"));
            
            // 区分清算报文、水报文
            if(IP.contains(bepsIP)){
                // 得到交易报文适配类集合
                ParserDealFactory msgDeal2Parser = FESDealConfig.getFESConfig()
                        .getMsgdeal2Parser();
                // 获取交易报文适配类对象
                MessageDeal messageDeal = (MessageDeal) msgDeal2Parser
                        .getdeal2ParserMap().get(Constant.BEPS22PMS);
                FESMessage messageValueObject = messageDeal.msgDeal(tempMsg,
                        Constant.BEPS22PMS);
                reqMsgHeader = messageValueObject.getReqMsgHeader();
                reqMsgBody = messageValueObject.getReqMsgBody();

                // 校验MAC码
                if (MsgMAC.checkMAC(tempMsg, 0)) {
                    //
                    ReqMsgHandlerItf itf = new PMSReqMsgHandler();
                    responseMsg = itf.paraseReqMsg(reqMsgHeader, reqMsgBody);
                    logger.info("\n业务处理后的响应内容:" + responseMsg);
                } else {

                    // MAC校验没有通过,则响应码为F005
                    responseMsg = ResponseType.NOT_PASS_MAC_CHECK;
                    // 日志跟踪
                    logger.info("\n请求方的MAC校验没有通过!");
                }
                byte[] responseMsgByte = ResSocketMsgHandlerImpl.createResMsg(
                        tempMsg, responseMsg, 0);

                int length = responseMsgByte.length;
                buf = IoBuffer.allocate(length);
                buf.put(responseMsgByte);
                buf.flip();
            }else if (IP.contains(epsIP)){
                // 获取服务IP
                String serviceIp = FESSocketConfig.getFESConfig().getLtWaterIp();
                // 获取服务端口
                int port = FESSocketConfig.getFESConfig().getLtWaterPort();
                SocketClients sc = new SocketClients(serviceIp, port);
                byte[] responseMsgByte = sc.relayMsg(bytes, FESConstant.SYDWID_WATER);
                int length = responseMsgByte.length;
                buf = IoBuffer.allocate(length);
                buf.put(responseMsgByte);
                buf.flip();
            }
            

        } catch (Exception e) {
            logger.error(LocalResource.getInfor("BUSINESS_FAIL_EXCEPTION"), e);
            // 数据操作异常
            // responseMsg = ResponseType.DATABASE_OPERATE_ERROR;
        }
        session.write(buf);
    }

    /**
     * 当消息传送到客户端后触发
     */
    public void messageSent(IoSession session, Object message) throws Exception {
        logger.info("服务端发送到客户端" + session.getRemoteAddress() + "信息成功...");
        /*
         * //如果需要短连接,就将下面方法放开
         */
        session.close();
    }

    /**
     * 当网络连接关闭时候,调用此方法
     */
    public void sessionClosed(IoSession session) throws Exception {
        // session.getRemoteAddress()=null
        logger.info("与客户端" + session.getRemoteAddress() + "断开连接!");
    }

    /**
     * // 当网络通道空闲时此方法被调用,在这里可以判断是读空闲、写空闲还是两个都空闲,以便做出正确的处理 //
     * 一半的网络通讯程序都要与服务器端保持长连接,所以这里可以发一下网络测试数据以保持与服务器端的连接
     */
    public void sessionIdle(IoSession session, IdleStatus status)
            throws Exception {
        // 当网络通道空闲时此方法被调用,在这里可以判断是读空闲、写空闲还是两个都空闲,以便做出正确的处理
        // 一半的网络通讯程序都要与服务器端保持长连接,所以这里可以发一下网络测试数据以保持与服务器端的连接

        logger.info("服务端进入空闲状态...");
    }

    /**
     * 当出现异常时,被调用该方法
     */
    public void exceptionCaught(IoSession session, Throwable cause)
            throws Exception {
        logger.error("服务端发送异常...", cause);
    }
}


-------------------------------------------------------------------------------------------

public class SocketServer {

    private String configFilePath;

    private ClassPathXmlApplicationContext context;

    public SocketServer() {
        this.configFilePath = "SocketServer.xml";
    }

    public SocketServer(String configFilePath) {
        this.configFilePath = configFilePath;
    }

    public static void main(String[] args) throws Exception {
        new SocketServer().start();
    }

    public void start() {
        context = new ClassPathXmlApplicationContext(configFilePath);
    }

    public void stop() {
        context.destroy();
    }
}


----------------------------------测试类--------------------------------------------------

【public class SocketServerHandlerTest {
    // 日志
    public static Logger logger = Logger.getLogger(SocketServerHandlerTest.class);

    /**
     * @param args
     */
    public static void main(String[] args) {

        new SocketServer("SocketServer.xml").start();

}



------------------------------------拦截器-----------------------------------------------------

package com.cvicse.ksccs.fes.socketserver;

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;


public class IntMsgLenghtProtocolDecoder extends CumulativeProtocolDecoder {

    @Override
    protected boolean doDecode(IoSession session, IoBuffer in,
            ProtocolDecoderOutput out) throws Exception {       

        byte[] bytes = new byte[in.limit()];
        in.get(bytes);
        IoBuffer buf = IoBuffer.allocate(in.limit());
        buf.put(bytes);
        buf.flip();
        out.write(buf);
        return true;

    }

}



package com.cvicse.ksccs.fes.socketserver;

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;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;

/**
 * @author han_feng
 *
 */
public class IntMsgLenghtProtocolCodecFactory implements ProtocolCodecFactory {

    private final MessageEncoder encoder;
    private final IntMsgLenghtProtocolDecoder decoder;

    public IntMsgLenghtProtocolCodecFactory() {
        encoder = new MessageEncoder();
        decoder = new IntMsgLenghtProtocolDecoder();
    }

    public ProtocolDecoder getDecoder(IoSession session) throws Exception {
        return decoder;
    }

    public ProtocolEncoder getEncoder(IoSession session) throws Exception {
        return encoder;
    }

    /**
     * 译码器,不做任何事情
     */
    private static class MessageEncoder extends ProtocolEncoderAdapter {
        public void encode(IoSession session, Object message,
                ProtocolEncoderOutput out) throws Exception {
        }
    }

}





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值