apache mina第三发-自定义通信协议(下)

    在实际的网络通信中,阻塞是很可能发生的。那我们可以设想如果我们的一条消息被分成了两截,分两次发来,那么我们的解码器中的doDecode方法被先后调用两次,在前一次中不足以解码出一条完整的信息,而在后一次调用中我们也不知道前面一次解码到了哪一步。这个问题我们通过引入一个上下文的概念来解决。

    在doDecode方法中有一个IoSession类型的参数,它很类似于Web中的Session,拥有一个Map,用键值对的形式来存储参数。一个IoSession可以类比为阻塞IO中的一个连接,我们可以把解码过程中用到的所有变量都存在里面。为了程序的结构良好,我们把所有变量包装成一个Context对象,变量的初始化都放在Context的构造函数里,原来对变量的操作也就成了对Context的操作。改造后的解码器类如下所示

package mina.common;

import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

/**
 * 解码器类
 * @author 尹定宇
 * @Email [email protected]
 * @version 2013-5-2 上午10:10:03
 * @info
 */
public class CmccSipcDecoder extends CumulativeProtocolDecoder {
	
	//字符集
	private final Charset charset;
	
	//上下文的key
	private final AttributeKey CONTEXT = new AttributeKey(getClass(),"context");
	
	//构造方法
	public CmccSipcDecoder(Charset charset) {
		this.charset = charset;
	}
	

	@Override
	protected boolean doDecode(IoSession session, IoBuffer in,
	ProtocolDecoderOutput out) throws Exception {
		
		//得到上下文
		Context ctx = getContext(session);
		
		//设置缓冲区,初始长度为100,可以自动调整长度
		IoBuffer buffer = ctx.getInnerBuffer();
		
		//获得一个字符编码器
		CharsetDecoder cd = charset.newDecoder();
		
		//表示一行中第几个字节
		int matchCount = ctx.getMatchCount();
		
		//初始化几个字符串
		String statusLine = ctx.getStatusLine(), 
				sender = ctx.getSender(),
				receiver = ctx.getReceiver(),
				length = ctx.getLength(),
				sms = ctx.getSms();
		
		//表示第几行
		int line = ctx.getLine();
		
		//从输入流中读取
		while (in.hasRemaining()) {
			
			byte b = in.get();
			//游标前移
			matchCount++;
			buffer.put(b);
			
			//读到了换行符,而且不是第五行
			if (b == 10 && line < 4) {
								
				//如果是第一行,得到状态行字符串
				if (line == 0) {
					buffer.flip();
					statusLine = buffer.getString(matchCount, cd);
					statusLine = statusLine.substring(0,
					statusLine.length() - 1);
					matchCount = 0;
					buffer.clear();
					ctx.setStatusLine(statusLine);
				}
				//发送方
				if (line == 1) {
					buffer.flip();
					sender = buffer.getString(matchCount, cd);
					sender = sender.substring(0, sender.length() - 1);
					matchCount = 0;
					buffer.clear();
					ctx.setSender(sender);
				}
				//接收方
				if (line == 2) {
					buffer.flip();
					receiver = buffer.getString(matchCount, cd);
					receiver = receiver.substring(0, receiver.length()-1);
					matchCount = 0;
					buffer.clear();
					ctx.setReceiver(receiver);
				}
				//短信的长度
				if (line == 3) {
					buffer.flip();
					length = buffer.getString(matchCount, cd);
					length = length.substring(0, length.length() - 1);
					matchCount = 0;
					buffer.clear();
					ctx.setLength(length);
					
				}
				
				//行数递增
				line++;
				
			} else if (line == 4) {	//如果是第五行,则读取短信内容
				
				
				//当读到的字节数等于上一行所定义的短信长度时
				if (matchCount == Long.parseLong(length.split(": ")[1]))
				{
					buffer.flip();
					sms = buffer.getString(matchCount, cd);
					ctx.setSms(sms);
					ctx.setMatchCount(matchCount);
					ctx.setLine(line);
					break;
				}			
			} 
			ctx.setMatchCount(matchCount);
			ctx.setLine(line);
		}
		
		if(ctx.getLine()==4&&Long.parseLong(ctx.getLength().split(":")[1])==ctx.getMatchCount()){
			SmsObject smsObject = new SmsObject();
			smsObject.setSender(sender.split(": ")[1]);
			smsObject.setReceiver(receiver.split(": ")[1]);
			smsObject.setMessage(sms);
			
			out.write(smsObject);
			ctx.reset();
			return true;
		}else{
			return false;
		}
	}
	
	private Context getContext(IoSession session){
		Context context = (Context)session.getAttribute(CONTEXT);
		if(context == null){
			context = new Context();
			session.setAttribute(CONTEXT, context);
		}
		return context;
	}
	
	/**
	 * 存储上下文的内部类
	 * @author 尹定宇
	 * @Email [email protected]
	 * @version 2013-5-7 下午12:57:02
	 * @info
	 */
	private class Context{
		
		private final IoBuffer innerBuffer;
		private String statusLine = "";
		private String sender = "";
		private String receiver = "";
		private String length = "";
		private String sms = "";
		
		public Context(){
			innerBuffer = IoBuffer.allocate(100).setAutoExpand(true);
		}
		
		private int matchCount = 0;
		private int line = 0;

		public String getStatusLine() {
			return statusLine;
		}
		public void setStatusLine(String statusLine) {
			this.statusLine = statusLine;
		}
		public String getSender() {
			return sender;
		}
		public void setSender(String sender) {
			this.sender = sender;
		}
		public String getReceiver() {
			return receiver;
		}
		public void setReceiver(String receiver) {
			this.receiver = receiver;
		}
		public String getLength() {
			return length;
		}
		public void setLength(String length) {
			this.length = length;
		}
		public String getSms() {
			return sms;
		}
		public void setSms(String sms) {
			this.sms = sms;
		}
		public int getMatchCount() {
			return matchCount;
		}
		public void setMatchCount(int matchCount) {
			this.matchCount = matchCount;
		}
		public int getLine() {
			return line;
		}
		public void setLine(int line) {
			this.line = line;
		}
		
		
		public void reset(){
			this.innerBuffer.clear();
			this.matchCount = 0;
			this.line = 0;
			this.statusLine = "";
			this.sender = "";
			this.receiver = "";
			this.length = "";
			this.sms = "";
		}
		public IoBuffer getInnerBuffer() {
			return innerBuffer;
		}
	}
}

 有一点特别需要我们注意,就是所有变量都来自Context的get方法,基本类型的变量改变之后别忘了把它们set回去,当然如果你把变量完全隐藏起来,只把外界可能用到的操作对外提供接口,那就更好了。

猜你喜欢

转载自ciciyin.iteye.com/blog/1870027
今日推荐