`
scholers
  • 浏览: 619695 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

MINA2收包中对粘包的处理

阅读更多
  MINA2中(MINA2 RC版本,MINA2.0正式版已经发布)服务端接受数据默认有一定长度的缓冲区(可以在启动的时候设置)。那么对于大报文,怎么处理呢?比如说超过1024,甚至更多?MINA2为了节省网络流量,提高处理效率,会将大报文自动拆分(可能是存放MINA2中的缓冲区里面):比如2048字节的报文,就会拆分成两次;那么在接受的时候,就有一个如何判断是完整报文的问题,或者说是一个拆包组包的问题。
  MINA2中初始化服务的时候是可以设置输入和输出的缓冲区的:
 
 
  acceptor.getSessionConfig().setReadBufferSize(1024);

    MINA2提供的案例是,在IoSession中设置一个类似于session,存在在当前IoSession中的全局变量,在此IoSession中有效。
 
private final AttributeKey TEST = new AttributeKey(getClass(), "TEST");
  

  大家都知道,通过 SOCKET TCP/IP传输过来的报文是不知道边界的,所以一般会约定在前端固定长度的字节加上报文长度,让SERVER来根据这个长度来确定整个报文的边界,在我前面的博文有提到。其实MINA2中有:
  prefixedDataAvailable(4) int
方法,来判断固定长度的报文长度,但是参数只能是1,2,4;该方法很好用。判断前四字节的整型值是否大于等于整个缓冲区的数据。可以方便的判断一次 messageReceived 过来的数据是否完整。(前提是自己设计的网络通讯协议前四字节等于发送数据的长度) ,如果你不是设定1,2,4字节来作为长度的话,那么就没辙了。
   在你的解码操作中,MINA2的缓冲区发多少次报文,你的decode方法就会调用多少次。
  上面设置了session之后,可以采用一个方法:
 
 /**
	 * 
	 * @param session
	 *            会话信息
	 * @return 返回session中的累积
	 */
	private Context getContext(IoSession session) {
		Context ctx = (Context) session.getAttribute(CONTEXT);
		if (ctx == null) {
			ctx = new Context();
			session.setAttribute(CONTEXT, ctx);
		}
		return ctx;
	}
 

然后在你的decode方法中,首先从session取出数据对象,进行拼接:
 
 Context ctx = getContext(session);

  // 先把当前buffer中的数据追加到Context的buffer当中
  ctx.append(ioBuffer);
  // 把position指向0位置,把limit指向原来的position位置
  IoBuffer buf = ctx.getBuffer();
  buf.flip();

 
接着读取每次报文的总长度:
// 读取消息头部分
byte[] bLeng = new byte[packHeadLength];
buf.get(bLeng);
int length = -1;
try {
  length = Integer.parseInt(new String(bLeng));
} catch (NumberFormatException ex) {
  ex.printStackTrace();
}
if (length > 0) {
  ctx.setMsgLength(length);
}


在读取到每次报文的长度之后,就接着循环判断BUF里面的字节数据是否已经全部接受完毕了,如果没有接受完毕,那么就不处理;下面是完整处理的代码:
 
while (buf.remaining() >= packHeadLength) {
	buf.mark();
	// 设置总长度
	if (ctx.getMsgLength() <= 0) {
		// 读取消息头部分
		byte[] bLeng = new byte[packHeadLength];
		buf.get(bLeng);
		int length = -1;
		try {
			length = Integer.parseInt(new String(bLeng));
			} catch (NumberFormatException ex) {
						ex.printStackTrace();
			}
			if (length > 0) {
			  ctx.setMsgLength(length);
			   }
			}


			// 读取消息头部分
			int length = ctx.getMsgLength();
			// 检查读取的包头是否正常,不正常的话清空buffer
			if (length < 0) { // || length > maxPackLength2) {
			buf.clear();
			out.write("ERROR!");
			break;
			// 读取正常的消息包,并写入输出流中,以便IoHandler进行处理
          } else if (length > packHeadLength && buf.remaining() >= length) {
			//完整的数据读取之后,就可以开始做你自己想做的操作了						
	} else {
	    // 如果消息包不完整
	    // 将指针重新移动消息头的起始位置
	    buf.reset();
	    break;
	}
     }
    if (buf.hasRemaining()) { // 如果有剩余的数据,则放入Session中
	   // 将数据移到buffer的最前面
             IoBuffer temp = IoBuffer.allocate(2048).setAutoExpand(
						true);
	   temp.put(buf);
	   temp.flip();
	   buf.clear();
	   buf.put(temp);

    } else { // 如果数据已经处理完毕,进行清空
	buf.clear();
}
 

为了便于操作,最好设置一个内部类:
private class Context {
		private final CharsetDecoder decoder;
		private IoBuffer buf;
		private int msgLength = 0;
		private int overflowPosition = 0;

		/**
		 * 
		 * 
		 */
		private Context() {
			decoder = charset.newDecoder();
			buf = IoBuffer.allocate(80).setAutoExpand(true);
		}

		/**
		 * 
		 * 
		 * @return CharsetDecoder
		 */
		public CharsetDecoder getDecoder() {
			return decoder;
		}

		/**
		 * 
		 * 
		 * @return IoBuffer
		 */
		public IoBuffer getBuffer() {
			return buf;
		}

		/**
		 * 
		 * 
		 * @return overflowPosition
		 */
		public int getOverflowPosition() {
			return overflowPosition;
		}

		/**
		 * 
		 *
		 * @return matchCount
		 */
		public int getMsgLength() {
			return msgLength;
		}

		/**
		 * 
		 * 
		 * @param matchCount
		 *            报文长度
		 */
		public void setMsgLength(int msgLength) {
			this.msgLength = msgLength;
		}

		/**
		 * 
		 * 
		 */
		public void reset() {
			this.buf.clear();
			this.overflowPosition = 0;
			this.msgLength = 0;
			this.decoder.reset();
		}

		/**
		 * 
		 * @param in
		 *            输入流
		 */
		public void append(IoBuffer in) {
			getBuffer().put(in);

		}

	}

 
分享到:
评论
1 楼 lzh910503 2015-03-13  
 

相关推荐

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

    本实例主要关注的是Mina在处理网络通信时遇到的两个常见问题——“断包”和“粘包”。 1. **断包**: 当数据在网络中传输时,可能会因为各种原因(如网络拥塞、数据包大小限制等)被分割成多个部分,这些部分在...

    mina 断包,粘包问题解决

    2. 分析服务端代码,找到自定义解码器的部分,理解它是如何处理粘包和断包的。 3. 查看客户端代码,了解数据编码过程,以及如何发送这些编码后的数据。 4. 运行服务端和客户端,进行实际测试,观察是否解决了断包和...

    mina 协议 解包 粘包

    mina 协议 解包 粘包

    mina的高级使用,mina文件图片传送,mina发送文件,mina报文处理,mina发送xml和json

    2. **Mina文件图片传送** 在Mina中,我们可以利用BufferedIoFilter或者IoBuffer来传输文件,包括图片。首先,我们需要将图片文件读取到内存中的IoBuffer,然后通过过滤器链传递给远程客户端。在这个过程中,可以...

    mina2技术知识

    MINA2的强大之处在于它为开发者提供了异步事件驱动的网络通信框架,免去了处理底层网络编程的复杂性,如多线程管理和数据缓冲等。 MINA2的核心概念包括: 1. **IoSession**:IoSession 是 MINA 中的核心接口,它...

    mina2核心框架

    MINA2作为其最新版本,尤其在处理大量并发连接时展现出了卓越的能力,如"mina2核心框架5000个并发"所示,它能够轻松应对高负载环境,为开发者提供了稳定且高效的网络编程解决方案。 MINA2的核心特性包括: 1. **...

    mina2推送demo客户端

    《MINA2推送Demo客户端详解及应用...通过对这个例子的深入学习和实践,开发者不仅可以掌握MINA2的基本用法,还能了解到如何利用MINA2构建高效、可靠的网络应用,这对于任何致力于网络编程的开发者来说都是宝贵的资源。

    服务端基于MINA2的UDP双向通信Demo演示(MINA2服务端)

    4. **MINA2的UDP服务端**:MINA2服务端会创建一个Acceptor监听特定的UDP端口,接收到数据后通过FilterChain进行处理,然后可能发送回相应的数据。 5. **过滤器(Filter)**:MINA2中的过滤器是处理输入和输出数据的...

    mina2.x开发示例

    **Mina2.x开发示例** Apache Mina是一个开源项目,提供了一个高度可扩展的网络通信框架,适用于各种协议,如TCP、UDP和HTTP。它主要用于简化开发高性能、高并发的网络应用程序,如服务器和客户端。在Mina2.x版本中...

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

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

    mina2 源码 mina

    标题"mina2 源码 mina"暗示我们将探讨MINA2的源代码,这是一个非常有价值的资源,对于理解MINA的工作原理、学习如何构建网络应用程序以及定制MINA的行为非常有用。MINA的源码包含了丰富的注释和示例,可以帮助开发者...

    springboot mina 串口对接

    在串口通信中,Mina可以处理底层的I/O操作,如打开串口、设置波特率、奇偶校验等,并提供了事件驱动模型,使得我们可以方便地监听和响应串口事件。 "粘包"和"断包"是网络通信中常见的问题。当数据包边界不清晰时,...

    mina2+spring结合实例

    Mina2作为一个轻量级的网络通信框架,提供了高度抽象的API,使得开发者可以方便地处理网络I/O操作。而Spring框架以其强大的依赖注入和面向切面编程特性,简化了Java应用的复杂性。当这两个框架相结合时,可以创建出...

    Java mina2源码

    通过深入研究Mina2的源码,我们可以了解到如何优化网络通信性能,如何处理大规模并发连接,以及如何设计和实现自己的网络协议。源码分析还能帮助我们理解Java的多线程、并发控制和事件驱动编程等高级特性,提升我们...

    给予mina 协议进行大数据传输

    2. **事件驱动**:MINA基于事件驱动的设计,通过监听网络事件(如连接建立、数据到达等)来触发相应的处理逻辑,简化了复杂网络应用的编程。 3. **异步通信**:MINA的异步通信模式意味着发送请求后无需等待响应,...

    MINA2 用户手册(中英文2个版本)加API(英文)

    5. **MINA2事件驱动模型**:MINA2基于事件驱动模型,通过事件触发器IoAdapter来处理网络事件,如连接建立、数据到达、连接关闭等。这种模型能有效利用多线程,提高并发处理能力。 6. **API文档**:英文版的MINA2 ...

    mina2资料-各种教程

    IoFilter则是一个过滤器链,允许在数据传输到IoHandler之前对其进行预处理或在之后进行后处理。通过IoFilter,你可以实现认证、加密、压缩等各种功能,增强了MINA的灵活性和可扩展性。 在MINA中,ByteBuffer的使用...

    mina2 实例程序(socket通讯调用远程方法)

    使用Mina2创建Socket连接,首先需要定义一个服务端的ProtocolHandler,处理接收到的数据。然后启动Acceptor监听特定端口,等待客户端连接。客户端通过Connector发起连接请求,连接成功后,双方都可以通过Session...

Global site tag (gtag.js) - Google Analytics