Apache MINA学习之路(3)

       在上一个程序中,还存在很多问题,其中最严重的一个为解码器中定义了成员变量 IoBuffer,但 Decoder 实例是单例的, 因此 Decoder实例中的成员变量可以被多线程共享访问,可能会因为变量的可见性而造成数据异常。

       每个IoSession都需要有自己的解码器实例;MINA确保同一时刻只有一个线程在执行decode() 函数——不允许多线程并发地执行解码函数,但它并不能保证每次解码过程都是同一线程在执行(两次解码用的可能是不同的线程) 。假设第一块数据被线程1管理,这时还没接收到足够的数据以供解码,当接收到第二块数据时,被另一个线程2管理,此时可能会出现变量的可视化(Visibility)问题。
       因此,每个IoSession都需要独立保存解码器所解码时未完成的数据。办法就是保存在IoSession的属性中,每次解码时,都先从它的属性中拿出上次未完成的任务数据,把新数据追加在它的后面。

  解决源码

import java.nio.charset.CharacterCodingException;
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.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

public class MyCodecDecoder implements ProtocolDecoder {
	private Charset charset = Charset.forName("utf-8");
	// 定义常量值,作为每个IoSession中保存解码内容的key值
	private static String CONTEXT = MyCodecDecoder.class.getName()
			+ ".context";

	public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out)
			throws Exception {
		Context ctx = getContext(session);
		decodeAuto(ctx, in, out);
	}

	private Context getContext(IoSession session) {
		Context ctx = (Context) session.getAttribute(CONTEXT);
		if (ctx == null) {
			ctx = new Context();
			//IoSession的Attribute使用用一个同步的HashMap保存对象
			session.setAttribute(CONTEXT, ctx);
		}
		return ctx;
	}

	private void decodeAuto(Context ctx, IoBuffer in, ProtocolDecoderOutput out)
			throws CharacterCodingException {
		boolean mark = false;
		while (in.hasRemaining()) {
			byte b = in.get();
			switch (b) {
			case '\r':
				break;
			case '\n':
				mark = true;
				break; // 跳出switch
			default:
				ctx.getBuf().put(b);
			}
			if (mark) {
				IoBuffer t_buf = ctx.getBuf();
				t_buf.flip();
				try {
					out.write(t_buf.getString(charset.newDecoder()));
				} finally {
					//clear:limit=capacity , position=0,重置mark;它是不清空数据,但从头开始存放数据做准备---相当于覆盖老数据
					//reset:清空数据
					t_buf.clear();
				}
			}
		}
	}

	public void dispose(IoSession session) throws Exception {
		Context ctx = (Context) session.getAttribute(CONTEXT);
		if (ctx != null) {
			session.removeAttribute(CONTEXT);
		}
	}

	public void finishDecode(IoSession session, ProtocolDecoderOutput out)
			throws Exception {
	}

	//内部类中有一个成员变量IoBuffer,用来存储每个IoSesssion解码的内容
	private class Context {
		private IoBuffer buf;

		public Context() {
			buf = IoBuffer.allocate(100).setAutoExpand(true);
		}

		public IoBuffer getBuf() {
			return buf;
		}
	}
}

 

你可能感兴趣的:(apache)