浅谈MINA的使用

来源:互联网 发布:smartgit mac 注册码 编辑:程序博客网 时间:2024/05/29 08:02

最近做了一个POS终端通信的小项目,主要是给POS终端提供TCP接口。之前的工作中没有从事相关的开发,也没涉及到网络通信的相关框架,在网上找了一些相关资料,最终选用了apache下的mina框架。

mina框架是一个网络通信框架,也就是它是基于TCP/IP、UDP/IP的协议栈的通信框架。mina对java的socket进行了封装,我们在使用mina时不用把过多的精力放在网络通信的开发上,mina给我们提供了事件驱动、异步它的异步默认使用的是java noi作为异步支持。同时mina对通信的客户端和服务端都进行了封装,我们可以轻松的使用mina进行服务端可客户端的开发。

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

(2.) IoProcessor:这个接口在另一个线程上,负责检查是否有数据在通道上读写,也就是说它也拥有自己的Selector,这是与我们使用JAVA NIO 编码时的一个不同之处,通常在JAVA NIO 编码中,我们都是使用一个Selector,也就是不区分IoService与IoProcessor 两个功能接口。另外,IoProcessor 负责调用注册在IoService 上的过滤器,并在过滤器链之后调用IoHandler。
(3.) IoFilter:这个接口定义一组拦截器,这些拦截器可以包括日志输出、黑名单过滤、数据的编码(write 方向)与解码(read 方向)等功能,其中数据的encode 与decode是最为重要的、也是你在使用Mina 时最主要关注的地方。
(4.) IoHandler:这个接口负责编写业务逻辑,也就是接收、发送数据的地方。

废话不多说,以下是我自己编写的服务端程序,贴出来供大家参考学习:

import java.net.InetSocketAddress;import org.apache.log4j.Logger;import org.apache.mina.core.session.IdleStatus;import org.apache.mina.filter.codec.ProtocolCodecFilter;import org.apache.mina.transport.socket.nio.NioSocketAcceptor;import com.chinaums.umscard.socket.codec.MsgCodecFactory;import com.chinaums.umscard.util.BaseConfig;public class MinaServer {private static final Logger log = Logger.getLogger(MinaServer.class);public static void main(String[] args) {// 创建一个非阻塞的server端的SocketNioSocketAcceptor acceptor = new NioSocketAcceptor();try {// 设置过滤器(使用自己封装的编解码器)acceptor.getFilterChain().addLast("codec",new ProtocolCodecFilter(new MsgCodecFactory("UTF-8")));// 设置读取数据的缓冲区大小为1Macceptor.getSessionConfig().setReadBufferSize(1024*1024);// 读写通道10秒内无操作进入空闲状态acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);// 绑定逻辑处理器acceptor.setHandler(new MinaServerHandler());// 绑定端口acceptor.bind(new InetSocketAddress(BaseConfig.PORT));log.info("服务端启动。。。。。端口号为:"+BaseConfig.PORT);} catch (Exception e) {e.printStackTrace();}}}

请注意,过滤器可以使用mina提供的过滤器,也可以自己根据需要编写过滤器。这里我使用的自己编写的过滤器
过滤器代码如下
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;public class MsgCodecFactory implements ProtocolCodecFactory {
//这里注册自己编写的解码工具private final MsgResponseEncoder encoder; // 编码private final MsgRequestDecoder decoder; // 解码public MsgCodecFactory(String charset) {encoder = new MsgResponseEncoder(charset);decoder = new MsgRequestDecoder(charset);}public ProtocolEncoder getEncoder(IoSession session) {return encoder;}public ProtocolDecoder getDecoder(IoSession session) {return decoder;}}
解码代码如下
import org.apache.log4j.Logger;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;import com.chinaums.umscard.util.StringUtils;public class MsgRequestDecoder extends CumulativeProtocolDecoder {private static final Logger LOGGER = Logger.getLogger(MsgRequestDecoder.class);private final String charset;public MsgRequestDecoder(String charset) {this.charset = charset;}/** *解码器,对传入的iobuffer 进行解码工作,注意顺序是先进先出原则。 */@Overrideprotected boolean doDecode(IoSession session, IoBuffer in,ProtocolDecoderOutput out) throws Exception {// TODO Auto-generated method stubif (in.remaining() > 2) {in.mark();// 标记当前位置,以便resetbyte[] lenBytes = new byte[3];in.get(lenBytes);int length = Integer.parseInt(StringUtils.hexStr2Str(StringUtils.bytes2HexString(lenBytes)));//int length = StringUtils.bytesToInt(lenBytes);if (length > in.remaining()) {// 如果消息内容不够,则重置,相当于不读取sizeSystem.out.println("package notenough  left=" + in.remaining()+ " length=" + length);in.reset();return false;// 接收新数据,以拼凑成完整数据} else {System.out.println("package =" + in.toString());byte[] bbIn = new byte[length];in.get(bbIn, 0, length);byte[] btotal = new byte[length];System.arraycopy(bbIn, 0, btotal, 0, bbIn.length);byte[] pwd = new byte[8];System.arraycopy(btotal, btotal.length-8, pwd, 0, 8);System.out.println(StringUtils.bytes2HexString(pwd));String result = StringUtils.bytes2HexString(btotal);out.write(result);if (in.remaining() > 0) {// 如果读取内容后还粘了包,就让父类再给一次,进行下一次解析// System.out.println("package left="+in.remaining()+" data="+in.toString());}return true;// 这里有两种情况1:没数据了,那么就结束当前调用,有数据就再次调用}}return false;// 处理成功,让父类进行接收下个包}}
import java.nio.charset.Charset;
import org.apache.mina.core.buffer.IoBuffer;import org.apache.mina.core.session.IoSession;import org.apache.mina.filter.codec.ProtocolEncoderAdapter;import org.apache.mina.filter.codec.ProtocolEncoderOutput;
public class MsgResponseEncoder extends ProtocolEncoderAdapter {  private final String charset; public MsgResponseEncoder(String charset) {  this.charset = charset; }  public void encode(IoSession session, Object message,   ProtocolEncoderOutput out) throws Exception {    IoBuffer buf = IoBuffer.allocate(100).setAutoExpand(true);  String strOut = message.toString();//  buf.putInt(strOut.getBytes(Charset.forName("utf-8")).length);  buf.putString(strOut,Charset.forName("GBK").newEncoder());  buf.flip();  out.write(buf); }}以下是业务处理类import java.util.List;import org.apache.log4j.Logger;import org.apache.mina.core.service.IoHandlerAdapter;import org.apache.mina.core.session.IdleStatus;import org.apache.mina.core.session.IoSession;import com.chinaums.umscard.entity.AccountInfo;import com.chinaums.umscard.entity.BasCardPos;import com.chinaums.umscard.entity.CardMeracc;import com.chinaums.umscard.entity.ConsumeParam;import com.chinaums.umscard.entity.DataAccReturn;import com.chinaums.umscard.entity.DataPosFlow;import com.chinaums.umscard.entity.PayTranPay;import com.chinaums.umscard.entity.RejectGoodsParam;import com.chinaums.umscard.service.AccountInfoService;import com.chinaums.umscard.service.CardMeraccService;import com.chinaums.umscard.service.ConsumeService;import com.chinaums.umscard.service.DataAccReturnService;import com.chinaums.umscard.service.DataFlowService;import com.chinaums.umscard.service.PosCheckService;import com.chinaums.umscard.service.RejectGoodsService;import com.chinaums.umscard.service.ReverseService;import com.chinaums.umscard.service.impl.AccountInfoServiceImpl;import com.chinaums.umscard.service.impl.CardMeraccServiceImpl;import com.chinaums.umscard.service.impl.ConsumeServiceImpl;import com.chinaums.umscard.service.impl.DataAccReturnServiceImpl;import com.chinaums.umscard.service.impl.DataFlowServiceImpl;import com.chinaums.umscard.service.impl.PosCheckServiceImpl;import com.chinaums.umscard.service.impl.RejectGoodsServiceImpl;import com.chinaums.umscard.service.impl.ReverseServiceImpl;import com.chinaums.umscard.util.MySecurityUtil;import com.chinaums.umscard.util.StringUtils;import com.chinaums.umscard.util.XorUtil;public class MinaServerHandler extends IoHandlerAdapter {public static final Logger log = Logger.getLogger(MinaServerHandler.class);@Overridepublic void sessionCreated(IoSession session) throws Exception {log.info("服务端与客户端创建连接...");}@Overridepublic void sessionOpened(IoSession session) throws Exception {log.info("服务端与客户端打开连接");}@Overridepublic void sessionClosed(IoSession session) throws Exception {log.info("服务端与客户端关闭连接");}
//这个方法用于接收监听接收到的报文数据,以进行相关的业务处理@Overridepublic void messageReceived(IoSession session, Object message) throws Exception{String m = message.toString();String bcd = m.substring(m.length()-16);//System.out.println(bcd);byte[] bt = message.toString().getBytes();byte[] keypwdbt = new byte[8];System.arraycopy(bt, bt.length-8, keypwdbt, 0, 8);String result = MySecurityUtil.bcd2Str(keypwdbt);String msg = StringUtils.hexStr2Str(message.toString());//PosCheckService posService = new PosCheckServiceImpl();PosCheckService posCheckService = new PosCheckServiceImpl();DataFlowService dataFlowService = new DataFlowServiceImpl();MySecurityUtil ms = new MySecurityUtil();//PosCheckService posService = new PosCheckServiceImpl();if(msg != null){String msgArr = msg.replace("|", ",");String[] posArr = msgArr.split(",");if(msg.toString().startsWith("0001")){String checkmsg = "";//用于接收签到结果String posno = posArr[1];String key = posArr[2];String keypwd = posArr[3];//异或加密后的验证码StringBuffer sb = new StringBuffer();sb.append(posArr[0]);sb.append("|");//以下将报文进行异或加密处理(不取最后一个元素)for(int i=1;i<posArr.length-1;i++){sb.append(posArr[i]+",");}System.out.println(sb);//异或运算byte[] b = XorUtil.xorChar(sb.toString().toCharArray());//加密byte[] key1 = MySecurityUtil.hexStr2Str(key.substring(0, key.length()/2)).getBytes();byte[] key2 = MySecurityUtil.hexStr2Str(key.substring(key.length()/2)).getBytes();byte[] p = ms.TripleDesEncrypt(b, key1, key2);//转成字符串String s = MySecurityUtil.bcd2Str(p);BasCardPos basCardPos = posCheckService.checkPos(posno);if(basCardPos != null && basCardPos.getIkey().equals(posArr[2])){checkmsg = "00";//签到成功}else if(basCardPos != null && basCardPos.getIkey() != posArr[2]){checkmsg = "99";//密钥验证失败}else{checkmsg = "01";//签到失败}//插入日志DataPosFlow dataPosFlow = new DataPosFlow();dataPosFlow.setPosno(posno);dataPosFlow.setVreturn(checkmsg);dataFlowService.PosCheckDataFlow(dataPosFlow);session.write(checkmsg+"#"+s);log.info(checkmsg);}else if(msg.toString().startsWith("0002")){AccountInfoService accService = new AccountInfoServiceImpl();String selectmsg = "";//用于接收查询账户返回的状态码//取磁道内容String cardnoecp = posArr[1];//取终端号String posno = posArr[2];//获取密钥String key = posArr[3];//获取密码String pwd = posArr[4];//第一步检查终端的合法性BasCardPos bcp = posCheckService.checkPos(posno);StringBuffer sb = new StringBuffer();if(bcp != null && bcp.getIkey().equals(key)){List<AccountInfo> accList = accService.checkAccount(cardnoecp);DataPosFlow dataPosFlow = null;if(accList != null && accList.size() != 0){selectmsg = "00";//查询成功sb.append(selectmsg+"#");for(AccountInfo account : accList){//String hexAccountName = new String(account.getAccname().getBytes("UTF-8"), "GB18030");sb.append(account.getCardid()+","+account.getCardno()+","+account.getAccid()+","+account.getAccname()+","+account.getBalance()+"|");dataPosFlow = new DataPosFlow();dataPosFlow.setCardid(account.getCardid());dataPosFlow.setAccid(account.getAccid());dataPosFlow.setPosno(posno);dataPosFlow.setVreturn(selectmsg);dataFlowService.AccountInfoDataFlow(dataPosFlow, account);}session.write(sb);}else{selectmsg = "98";session.write(selectmsg+"#");}}else if(bcp != null && !key.equals(bcp.getIkey())){selectmsg = "99";session.write(selectmsg+"#");}log.info(selectmsg);}else if(msg.toString().startsWith("0003")){ConsumeService consumeService = new ConsumeServiceImpl();String consumemsg = "";//获取cardidString cardid = posArr[1];//获取accidString accid = posArr[2];String trantype = posArr[3];String amt = posArr[4];//获取终端号String posno = posArr[5];//获取密钥String key = posArr[6];String pwd = posArr[7];String possno = posArr[8];//String field1 = posArr[8];ConsumeParam param = new ConsumeParam();param.setCardid(Float.parseFloat(cardid));param.setAccid(3f);param.setAmt(Float.parseFloat(amt)/100);param.setPosno(posno);param.setPossno(possno);param.setTrainType(Float.parseFloat(trantype));BasCardPos bcp = posCheckService.checkPos(posno);//终端验证成功,验证卡是否可以在终端上使用if(bcp != null && key.equals(bcp.getIkey())){int count = consumeService.poscount(Integer.parseInt(cardid), Integer.parseInt(accid));//count不为0说明卡片可以在终端上使用if(count != 0){consumemsg = consumeService.consume(param);}else if(key != bcp.getIkey()){consumemsg = "99";//密钥验证失败}else if(count == 0){consumemsg = "98";//不存在卡}else{consumemsg = "97";//密码错误}}session.write(consumemsg+"#");log.info(consumemsg);}else if(msg.toString().startsWith("0004")){String rejectmsg = "";String cardid = posArr[1];String rejectTranType = posArr[2];String rejectPosno = posArr[3];String rejectkey = posArr[4];String rejectpwd = posArr[5];String rejectTranId = posArr[6];RejectGoodsParam param = new RejectGoodsParam();param.setCardid(Integer.parseInt(cardid));param.setKey(rejectkey);param.setPosno(rejectPosno);param.setPwd(rejectpwd);param.setTranid(rejectTranId);param.setTranType(Integer.parseInt(rejectTranType));//退货BasCardPos bcp = posCheckService.checkPos(rejectPosno);RejectGoodsService rgService = new RejectGoodsServiceImpl();DataAccReturnService dataAccReturnService = new DataAccReturnServiceImpl();if(bcp != null && rejectkey.equals(bcp.getIkey())){//根据传入的信息查询用户的卡信息和原单号,以便验证交易信息是否属实//验证成功,查询账户信息,返回账户信息rejectmsg = rgService.rejectGoods(param);//交易信息存在返回00向退货申请表中写入退货申请if (rejectmsg.equals("00")) {//查询交易记录PayTranPay pay = rgService.getPayTranPay(param);//验证成功将退货请求写入退货请求表DataAccReturn dataAccReturn = new DataAccReturn();dataAccReturn.setCashierno(rejectPosno);dataAccReturn.setTranid(rejectTranId);dataAccReturn.setPosno(rejectPosno);dataAccReturn.setCardid(Integer.parseInt(cardid));dataAccReturn.setAccid(Integer.parseInt(pay.getPaysubtype()));dataAccReturn.setAmt(pay.getAmt());dataAccReturn.setPosno(rejectPosno);dataAccReturnService.addDataAccReturn(dataAccReturn);}}else if(bcp != null && !rejectkey.equals(bcp.getIkey())){rejectmsg = "99";}else{rejectmsg = "97";}CardMeraccService cardMeraccService = new CardMeraccServiceImpl();CardMeracc cardm = cardMeraccService.getCardMeraccByCardId(Integer.parseInt(cardid));DataPosFlow dataPosFlow = new DataPosFlow();dataPosFlow.setCardid(Integer.parseInt(cardid));dataPosFlow.setPosno(rejectPosno);dataPosFlow.setVreturn(rejectmsg);//写入日志dataFlowService.RejectGoodsDataPosFlow(dataPosFlow, cardm);session.write(rejectmsg+"#");}else if(msg.toString().startsWith("0005")){String reveremsg = "";//pos终端号String posno = posArr[1];//密钥String key = posArr[2];//批次号(POS终端生成)String posSno = posArr[3];//预留字段//String field1 = posArr[4];ReverseService reverserService = new ReverseServiceImpl();reveremsg = reverserService.reverse(posSno, posno);session.write(reveremsg+"#");}}log.info("服务端收到的数据为:"+message);}@Overridepublic void messageSent(IoSession session, Object message) throws Exception {log.info("服务端发送消息成功!");}@Overridepublic void sessionIdle(IoSession session, IdleStatus status) throws Exception {log.info("服务端进入空闲状态...");}@Overridepublic void exceptionCaught(IoSession session, Throwable cause) throws Exception {log.error("服务端发送异常...", cause);}}
至此服务端代码编写完毕,下面给一段客户端的代码。一般我们测试时用TCPUDP测试工具进行测试,不必要编写客户端程序,为了让大家有个参考,这里我把代码贴出来,只是简单的客户端程序,代码如下
import java.net.InetAddress;import java.net.InetSocketAddress;import java.net.UnknownHostException;import java.nio.charset.Charset;import org.apache.log4j.Logger;import org.apache.mina.core.future.ConnectFuture;import org.apache.mina.core.service.IoConnector;import org.apache.mina.core.session.IoSession;import org.apache.mina.filter.codec.ProtocolCodecFilter;import org.apache.mina.filter.codec.textline.LineDelimiter;import org.apache.mina.filter.codec.textline.TextLineCodecFactory;import org.apache.mina.transport.socket.nio.NioSocketConnector;public class MinaClient {private static final Logger logger = Logger.getLogger(MinaClient.class);//private static String ip = address.getpublic static void main(String[] args) throws UnknownHostException {InetAddress address = InetAddress.getLocalHost();String ip = address.getHostAddress().toString();// 创建一个非阻塞的客户端程序IoConnector connector = new NioSocketConnector();// 设置链接超时时间connector.setConnectTimeout(30000);// 添加过滤器connector.getFilterChain().addLast("codec",new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"), LineDelimiter.WINDOWS.getValue(),LineDelimiter.WINDOWS.getValue())));// 添加业务逻辑处理器类connector.setHandler(new MinaClientHandler());IoSession session = null;try {ConnectFuture future = connector.connect(new InetSocketAddress(ip, 2000));// 创建连接future.awaitUninterruptibly();// 等待连接创建完成session = future.getSession();// 获得session//Scanner s = new Scanner(System.in);//String msg = s.next();session.write("0001|10003,44444444444444444444444444444444,null");// 发送消息session.write("quit");//发送后与服务端断开连接} catch (Exception e) {logger.error("客户端链接异常...", e);}//session.getCloseFuture().awaitUninterruptibly();//等待连接断开connector.dispose();}}
编写客户端监听业务处理Handler类
import org.apache.log4j.Logger;import org.apache.mina.core.service.IoHandlerAdapter;import org.apache.mina.core.session.IoSession;public class MinaClientHandler extends IoHandlerAdapter {private static Logger logger = Logger.getLogger(MinaClientHandler.class);@Overridepublic void messageReceived(IoSession session, Object message)throws Exception {String msg = message.toString();logger.info("客户端接收到的信息为:" + msg);}@Overridepublic void exceptionCaught(IoSession session, Throwable cause)throws Exception {logger.error("客户端发生异常...", cause);}}
代码全部编写完毕,有兴趣的童鞋们请关注,另转载请注明出处http://blog.csdn.net/yuan16423276
以上代码或有不妥之处,欢迎大家多多指正,我将对其中的一些地方进行适当的优化。  

 


3 0
原创粉丝点击