mina开发总结

情景:

          公司产品,之前的设备协议与现在的设备协议都是采用字符串协议,但是不同的是,一种采用(数据+换行符)而另一种采用(数据长度+真正数据的方式),对于前者,mina有自带的codec(编码/解码器),对于后者,我们需要自己来处理

代码如下:

@SuppressWarnings("deprecation")
public class MessageDecoder extends CumulativeProtocolDecoder{
    //打印日志信息
//    private final static Logger log = LoggerFactory
//            .getLogger(MessageDecoder.class);
	private static final int PACKHEAD_LENTH = 6;//6位的数据包头长度

	@Override
    protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
//    	int remaining = in.remaining();
//    	byte[] dst = new byte[remaining];
//		in.get(dst );
//    	String str = new String(dst,"UTF-8");
//    	if(str.length()==6){//6位的数据头
//    		
//    	}
    	
    	if(in.remaining() > PACKHEAD_LENTH){//说明缓冲区有数据
    		//1.标记当前position,以便后继的reset操作能恢复position位置
    		in.mark();
    		//2.解码数据为字符串
    		String string = in.getString(new UTF8Decoder());//读取字符串
    		//3.包长度判断
    		Integer pakageLength = resolvePakageHead(string);
    		if(pakageLength==null){
    			//直接丢弃
    			return false;//等待数据
    		}
    		//4.包头与包身长度相等
    		if(pakageLength==string.substring(PACKHEAD_LENTH).length()){
    			 out.write(string); //发送给上层
    			 return false;
    		}
    		//5.包头>包身长度 有数据未送达	
    		if(pakageLength>string.substring(PACKHEAD_LENTH).length()){
    			in.reset();// 重置position到操作前
    			return false;//断包  等待新数据到来
    		}
    		//6.包头<包身  数据足够
    		if(pakageLength<string.substring(PACKHEAD_LENTH).length()){
    			in.reset();// 重置position到操作前
    			byte[] dst = new byte[pakageLength+PACKHEAD_LENTH];
				in.get(dst );
    			String str = new String(dst,"UTF-8");
    			out.write(str); //发送给上层
    			doDecode(session,in,out);//粘包递归处理
    			return false;//等待数据
    		}
    	}
    	
    	return false;  //等待数据不提交给上层处理
    	
    	//包头长度(int 的长度) 根据自定义协议的包头的长度
//        int packHeadLenth = "000000".getBytes().length;  
//        if(in.remaining() > packHeadLenth){  //说明缓冲区中有数据
//            in.mark();//标记当前position,以便后继的reset操作能恢复position位置
//
//            //获取数据包长度
//            int len = in.getInt();
//            log.info("len = "+len);
//
//            //上面的get会改变remaining()的值
//
//            if(in.remaining() <len - packHeadLenth) {
//                //内容不够, 重置position到操作前,进行下一轮接受新数据
//                in.reset();
//                return false;
//            }else{
//                //内容足够
//                in.reset(); //重置回复position位置到操作前
//                byte[] packArray = new byte[len]; 
//                in.get(packArray, 0, len); //获取整条报文
//
//                //根据自己需要解析接收到的东西  我的例子 把收到的报文转成String
//                String str = new String(packArray);
//                out.write(str); //发送出去 就算完成了
//
//                if(in.remaining() > 0){//如果读取一个完整包内容后还粘了包,就让父类再调用一次,进行下一次解析
//                    return true;
//                }
//            }
//        }
        
    }
    /**
     * 解析数据包头
     * @param string
     * @return
     */
	private Integer resolvePakageHead(String string) {
		try {
			return Integer.valueOf(string.substring(0,PACKHEAD_LENTH));//包长度
		} catch (Exception e) {
			// TODO: handle exception
		}
		return null;
	}
	
	
}
/**
 * 消息发送的编码器
 * @author Administrator
 *
 */
public class MessageEnCoder extends ProtocolEncoderAdapter{
    //用于打印日志信息
//    private final static Logger log = LoggerFactory
//            .getLogger(MessageEnCoder.class);
    //编码 将数据包转成字节数组
    @Override
    public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws Exception {

    	//message = EncodeAndDecodeMsg.object2Mgs(message);
        String value = (message == null ? "" : (String)message);
        IoBuffer buf = IoBuffer.allocate(value.length()).setAutoExpand(true);
        buf.put(value.getBytes("UTF-8"));
        buf.flip();
        out.write(buf);
        out.flush();
    	
    }
}

 

需要解决的问题

1.tcp连接数过多(该问题有以下问题导致)

2.清除不活跃的tcp

3.tcp的及时释放(加速四次挥手,项目可以快速重启,不然报端口占用)(setSoLinger配置解决)

所以以上问题就归结到,清除不活跃的tcp,或者说空闲的连接 和 快速关闭连接

解决方案:

         我们这里的场景是(物联网方面),设备每隔1分钟,会发送心跳信息。

         代码:

            

   @Override
    public void sessionCreated(IoSession session) throws Exception {
    	super.sessionCreated(session);
    	//及时关掉tcp  处于FIN_WAIT 状态的连接
    	SocketSessionConfig cfg = (SocketSessionConfig) session.getConfig();
//    	cfg.setReceiveBufferSize(2 * 1024 * 1024);
//		cfg.setReadBufferSize(2 * 1024 * 1024);
    	cfg.setSendBufferSize(1024*1024*100);
		cfg.setKeepAlive(true);
		cfg.setSoLinger(0); //这个是根本解决问题的设置
		//cfg.setOobInline(false);//关闭紧急数据接受,用于优先处理数据包
		cfg.setIdleTime(IdleStatus.BOTH_IDLE, 2*60);//2个心跳包的空闲,用于处理不活跃的tcp连接(设备离线,tcp未关闭),超时时间到,触发下面的方法
    }
	@Override
    public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
        super.sessionIdle(session, status);
        session.closeOnFlush();//关闭空闲连接
    }

mina端对于不活跃的tcp连接,tcp状态容易从keepalive----->on  。导致数据存在于发送缓冲区,不能即时发送,

解决:

         1.要么添加心跳

         2.这里也是模拟方法1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值