`
freemart
  • 浏览: 20433 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

mina粘包、多包和少包的解决方法

阅读更多
   使用过mina的同学应该都遇到到过,在解码时少包、多包的问题,查阅了很多资料还是迷迷糊糊的,经过

不懈努力,终于解决了。原来解决方法是那样的简单。废话少说,请看列子。
  
   另外建了一个交流群:19702042,大家可以在线交流

   问题:我发送的是xml字符串数据,在发送数据后,接收方在解码的时候可能接到1条,也可能是多条,还

可能是半条或一条半,解决方法就是使用CumulativeProtocolDecoder

   首先,在编码的时候要把前4位设成标志位,标志消息内容的长度。里面的重点是doDecode的返回值,一

定要继承CumulativeProtocolDecoder 哦。

   清看decode的写法:
public class AsResponseDecoder extends CumulativeProtocolDecoder {
	private static Logger LOG = LoggerFactory.getLogger(AsResponseDecoder.class);
	private final Charset charset;
	
	public AsResponseDecoder(Charset charset){
		this.charset = charset;
	}
	

	/**
	 * 这个方法的返回值是重点:
	 * 1、当内容刚好时,返回false,告知父类接收下一批内容
	 * 2、内容不够时需要下一批发过来的内容,此时返回false,这样父类

CumulativeProtocolDecoder
	 *    会将内容放进IoSession中,等下次来数据后就自动拼装再交给本类的doDecode
	 * 3、当内容多时,返回true,因为需要再将本批数据进行读取,父类会将剩余的数据再次推送本

类的doDecode
	 */
	public boolean doDecode(IoSession session, IoBuffer in,
			ProtocolDecoderOutput out) throws Exception {
		
		CharsetDecoder cd = charset.newDecoder();
		if(in.remaining() > 0){//有数据时,读取4字节判断消息长度
			byte [] sizeBytes = new byte[4];
			in.mark();//标记当前位置,以便reset
			in.get(sizeBytes);//读取前4字节
                        //NumberUtil是自己写的一个int转byte[]的一个工具类
			int size = NumberUtil.byteArrayToInt(sizeBytes);
			//如果消息内容的长度不够则直接返回true
			if(size > in.remaining()){//如果消息内容不够,则重置,相当于不读取size
				in.reset();
				return false;//接收新数据,以拼凑成完整数据
			} else{
				byte[] bytes = new byte[size]; 
				in.get(bytes, 0, size);
				String xmlStr = new String(bytes,"UTF-8");
				System.out.println("------------"+xmlStr);
				if(null != xmlStr && xmlStr.length() > 0){
					AsResponse resCmd = new AsResponse();
					AsXmlPacker.parse(resCmd, xmlStr);
					if(resCmd != null){
						out.write(resCmd);
					}
				}
				if(in.remaining() > 0){//如果读取内容后还粘了包,就让父类再给俺

一次,进行下一次解析
					return true;
				}
			}
		}
		return false;//处理成功,让父类进行接收下个包
	}


}


下面附上Encode类
public class AsResponseEncoder extends ProtocolEncoderAdapter {
	private final Charset charset;
	
	public AsResponseEncoder(Charset charset){
		this.charset = charset;
	}
	
	public void encode(IoSession session, Object message,
		ProtocolEncoderOutput out) throws Exception {
		CharsetEncoder ce = charset.newEncoder();
		IoBuffer buffer = IoBuffer.allocate(100).setAutoExpand(true);
		
		AsResponse respCmd = (AsResponse) message;
		
		String xml = AsXmlPacker.pack(respCmd);//将对象转成xml
		byte[] bytes = xml.getBytes();
		byte[] sizeBytes = NumberUtil.intToByteArray(bytes.length);
		
		buffer.put(sizeBytes);//将前4位设置成数据体的字节长度
		buffer.put(bytes);//消息内容
		buffer.flip();
		out.write(buffer);
	}


}

3
0
分享到:
评论
12 楼 c_m_l 2014-08-15  
@Override
	protected boolean doDecode(IoSession session, IoBuffer in,
			ProtocolDecoderOutput out) throws Exception {
		//判断前四字节的整型值是否大于等于整个缓冲区的数据。可以方便的判断一次 
		//messageReceived 过来的数据是否完整。
		
		if (in.prefixedDataAvailable(4, maxSize)) {
                     //在这里面做处理
                }


mina已经提供了这些一些列的做法 了, 可以使用in.prefixedDataAvailable(4, maxSize);这个API,很好用
11 楼 simple1024 2013-06-26  
kennyZhong 写道
if(in.remaining() > 0){//有数据时,读取4字节判断消息长度 
            byte [] sizeBytes = new byte[4]; 
            in.mark();//标记当前位置,以便reset 
            in.get(sizeBytes);//读取前4字节 
                        //NumberUtil是自己写的一个int转byte[]的一个工具类 
            int size = NumberUtil.byteArrayToInt(sizeBytes); 
            //如果消息内容的长度不够则直接返回true 
            if(size > in.remaining()){//如果消息内容不够,则重置,相当于不读取size 
                in.reset(); 
                return false;//接收新数据,以拼凑成完整数据 
            } else{ 
                byte[] bytes = new byte[size];  
                in.get(bytes, 0, size); 
                String xmlStr = new String(bytes,"UTF-8"); 
                System.out.println("------------"+xmlStr); 
                if(null != xmlStr && xmlStr.length() > 0){ 
                    AsResponse resCmd = new AsResponse(); 
                    AsXmlPacker.parse(resCmd, xmlStr); 
                    if(resCmd != null){ 
                        out.write(resCmd); 
                    } 
                } 
                if(in.remaining() > 0){//如果读取内容后还粘了包,就让父类再给俺 
 
一次,进行下一次解析 
                    return true; 
                } 
            } 
        } 
        return false;//处理成功,让父类进行接收下个包 





这段有问题吧?
那如果我的包是分成两段以上,后面读取的数据不就乱了吗?



怎么会乱呢?
如果数据不完整(断包了)那不就是返回false,继续接收下个包的数据,然后拼装起来,再次进行doDecode解析,如果还不够就会继续接收,直到出现一个完整的包(非size > in.remaining()),就会执行第一个else中的代码,封装对象,然后write出去。

在这之后再判断是不是有多余的数据(粘包了),如果有,返回true再次继续doDecode方法进行对buffer的解析。

只要你每次解析的数据是按照你所定义的,就不会出现问题。
如果出1个字节的错误,那可就全错了。

看完这些注释我恍然大悟。
谢谢哥们儿的帖子。
10 楼 kennyZhong 2013-06-25  
if(in.remaining() > 0){//有数据时,读取4字节判断消息长度 
            byte [] sizeBytes = new byte[4]; 
            in.mark();//标记当前位置,以便reset 
            in.get(sizeBytes);//读取前4字节 
                        //NumberUtil是自己写的一个int转byte[]的一个工具类 
            int size = NumberUtil.byteArrayToInt(sizeBytes); 
            //如果消息内容的长度不够则直接返回true 
            if(size > in.remaining()){//如果消息内容不够,则重置,相当于不读取size 
                in.reset(); 
                return false;//接收新数据,以拼凑成完整数据 
            } else{ 
                byte[] bytes = new byte[size];  
                in.get(bytes, 0, size); 
                String xmlStr = new String(bytes,"UTF-8"); 
                System.out.println("------------"+xmlStr); 
                if(null != xmlStr && xmlStr.length() > 0){ 
                    AsResponse resCmd = new AsResponse(); 
                    AsXmlPacker.parse(resCmd, xmlStr); 
                    if(resCmd != null){ 
                        out.write(resCmd); 
                    } 
                } 
                if(in.remaining() > 0){//如果读取内容后还粘了包,就让父类再给俺 
 
一次,进行下一次解析 
                    return true; 
                } 
            } 
        } 
        return false;//处理成功,让父类进行接收下个包 





这段有问题吧?
那如果我的包是分成两段以上,后面读取的数据不就乱了吗?
9 楼 liuwenbo200285 2013-04-13  
不需要同步吗?
8 楼 xiaogezq0 2012-08-01  
解决我打问题了,谢谢啊
7 楼 tianlovv 2012-06-01  
实现MessageDecoder也可以实现拆分包,个人感觉mina解码器这块有点乱。另外楼主的if(in.remaining() > 0)换成while是不更好些
6 楼 wszyquan 2012-03-11  
  
强烈支持楼主,写的很好,处理数据包的方式也很得当,很有帮助
感谢
5 楼 jiecooly 2011-12-21  
谢谢分享,这个是我参照你的改的。(给需要的人)
@Override
protected boolean doDecode(IoSession iosession, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
CharsetDecoder charsetDecoder = charset.newDecoder();
int msgLen = ServerConfiguration.MSG_HEADER_LEN;//4字节判断消息长度 
        if(in.remaining() > msgLen){
            in.mark();
            int length = in.getInt();
            if(length - msgLen > in.remaining()){
                in.reset(); 
                return false;
            } else{
            int limit = in.limit();
            in.limit(in.position() + length - msgLen);
String content = in.getString(charsetDecoder);
                out.write(content);
                in.limit(limit);
                if(in.remaining() > 0){
                    return true; 
                } 
            } 
        } 
        return false;
}
4 楼 jiangpingcmt1 2011-09-14  
这段程序确实写的好 粘包position用的精妙,我感觉应该加上if (sizeBytes[5] == 0) {// 长度小于4位要 接收新数据,以拼凑成完整数据。
logger.info("数据长度小于6位,接收新数据,以拼凑成完整数据.");
in.reset();
return false;// 接收新数据,以拼凑成完整数据
}
3 楼 qingming.com 2011-08-21  
字节流通讯,跟语言无关吧。
另:只是不知道CumulativeProtocolDecoder怎么处理粘包的,如果放在IoSession里,需要多线程同步吗?
2 楼 choclover 2011-07-26  
思路是不错的,只是如果客户端是C/Java socket直接连接等,不知道你server端这样的协议,如何办?
1 楼 leaya00 2011-07-14  
  
解决我的大问题了。呵呵。再次称赞下mina

相关推荐

    mina 断包,粘包问题解决

    总的来说,Apache Mina为解决网络通信中的断包和粘包问题提供了强大的工具。通过理解和应用mina_optimize代码,你可以更好地掌握如何在实际项目中规避这些问题,提升网络通信的稳定性和可靠性。在实际开发中,结合...

    Mina断包,粘包处理(完整实例,有客户端,服务端)

    总的来说,理解和处理Mina中的断包和粘包问题,需要深入理解网络通信协议、数据编码解码原理以及Mina框架的工作机制。通过分析Android-Mina-master项目中的代码,我们可以学习到如何在实际应用中解决这些问题,提高...

    mina 协议 解包 粘包

    mina 协议 解包 粘包

    springboot mina 串口对接

    综上所述,SpringBoot与Mina的结合,使我们能够方便地在Java应用程序中实现串口通信,解决粘包和断包问题,同时可能借助WebSocket实现与Web客户端的交互。"treatment_serial_service"作为核心组件,是串口通信功能的...

    mina4-android:Android TCP框架(基于MINA 2.0.0-M3)、增加Bytes工厂、无需依赖slf4j(新增Mina4Log打印输出日志), 处理Bytes粘包、半包、断包(ByteArrayDecoder)

    处理Bytes粘包、半包、断包(ByteArrayDecoder),需配置自己的首尾标识符, 如果与首尾标识符相同的数据出现在首尾标识符以内的范围,建议将该数据进行转义, 如这样配置转义规则(假设首尾标识符是0x7e): 0x7e = 0x...

    HPsocket 封包与mina对接

    ### HPsocket 封包与mina对接 #### 一、HPsocket概述 ...通过设置合适的包头标识、包体长度和缓冲区大小等参数,以及在Mina中正确配置过滤器和处理器,可以有效地解决断包、粘包等问题,实现稳定可靠的网络通信。

    Java视频教程 Java游戏服务器端开发 Netty NIO AIO Mina视频教程

    jaca视频教程 jaca游戏服务器端开发 Netty NIO AIO Mina视频教程 课程目录: 一、Netty快速入门教程 01、第一课NIO 02、第二课netty服务端 03、第三课netty客户端 04、第四课netty线程模型源码分析(一) 05、...

    Voovan开发手册

    - **起源**:Voovan项目起源于开发者在使用Netty和Mina框架过程中遇到的一些难点和困惑,尤其是关于粘包等问题的处理。这促使开发者进一步研究Java异步通信机制,并最终决定基于Java 1.7之后引入的AIO(Asynchronous...

    面向智能相机的远程异步图像数据处理服务.pdf

    为了解决这一问题,南京邮电大学的研究人员潘钺、胡健和孟庆琳提出了一种面向智能相机的远程异步图像数据处理服务,旨在通过服务器端对智能相机并发图像数据进行处理,并提供了一个整体的服务框架。 这项服务首先在...

    Java Netty 分布式开发 框架

    Netty的组件和设计使其在分布式系统的网络通信中表现出色,比如处理消息分段、粘包和半包问题、心跳检测、重连机制等。它的扩展性允许开发者针对特定的业务需求开发插件。例如,在处理用户登录时,可以添加一个...

    Java面试题10道Java高级必备的Netty面试题!.pdf

    - 如何处理粘包和拆包问题,以及如何选择合适的FrameDecoder? 6. **零拷贝** - 什么是零拷贝?Netty是如何实现零拷贝的? - 零拷贝在什么场景下能显著提高性能? 7. **心跳机制** - 在Netty中如何实现心跳...

    Java Netty-入门教程.pdf

    - 对常见的问题如粘包、半包现象提供了有效的解决方案。 - 避免了 epoll 空轮询导致的 CPU 资源浪费问题。 - 对 Java 标准库中的 API 进行了增强和优化,提高了使用的便利性和效率。 - **相比于其他网络应用框架*...

Global site tag (gtag.js) - Google Analytics