Java - Apache Mina 自定義協議通訊

襲冷發表於2014-06-30

一、定義協議實體

import java.nio.charset.Charset;

/**
 * 自定義協議的訊息體
 */
public class MyMsg {
	/**
	 * 訊息長度
	 */
	private Integer lenth;

	/**
	 * 傳送人
	 */
	private Long sender;

	/**
	 * 接收人
	 */
	private Long receiver;

	/**
	 * 訊息內容
	 */
	private String content;
	
	public MyMsg() {
		
	}

	public Long getSender() {
		return sender;
	}

	public void setSender(Long sender) {
		this.sender = sender;
	}

	public Long getReceiver() {
		return receiver;
	}

	public void setReceiver(Long receiver) {
		this.receiver = receiver;
	}

	public String getContent() {
		return content;
	}

	public void setContent(String content) {
		this.content = content;
	}

	/**
	 * 先計算長度,再返回。這裡長度包含長度本身的位元組
	 */
	public Integer getLenth() {
		
		this.lenth = 4 + 8*2 + this.content.getBytes(Charset.forName("utf-8")).length;
		
		return lenth;
	}

	public MyMsg(Long sender, Long receiver, String content) {
		this.sender = sender;
		this.receiver = receiver;
		this.content = content;
	}

	@Override
	public String toString() {
		return "MyMsg [lenth=" + this.getLenth() + ", sender=" + sender + ", receiver="
				+ receiver + ", content=" + content + "]";
	}

}
二、定義編解碼器

    1、編碼器

import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;

/**
 *	編碼器 
 */
public class MyEncoder extends ProtocolEncoderAdapter {

	@Override
	public void encode(IoSession session, Object message, ProtocolEncoderOutput encoderOutput)
			throws Exception {

		CharsetEncoder ce = Charset.forName("utf-8").newEncoder();
		
		MyMsg msg = (MyMsg) message;
		
		//	Mina IoBuffer
		IoBuffer buffer = IoBuffer.allocate(100).setAutoExpand(true);
		
		buffer.putInt(msg.getLenth());
		buffer.putLong(msg.getSender());
		buffer.putLong(msg.getReceiver());
		
		//	有多個可變長度的屬性時,可約定通過定義可變屬性的最大長度(多餘擷取不足補齊)或put之前put其長度等方式處理
		buffer.putString(msg.getContent(), ce);
		
		buffer.flip();
		
		encoderOutput.write(buffer);
		
	}

}
    2、解碼器

import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;

/**
 *	解碼器可繼承ProtocolEncoderAdapter,但它不利於處理粘包的情況
 */
public class MyDecoder extends CumulativeProtocolDecoder {
	
	 @Override
	protected boolean doDecode(IoSession session, IoBuffer buffer, ProtocolDecoderOutput decoderOutput) 
			throws Exception {
		CharsetDecoder de = Charset.forName("utf-8").newDecoder();

		/**
		 * 這裡,如果長度不夠20位元組,就認為這條訊息還沒有累積完成
		 */
		if(buffer.remaining() < 20){
			/**
			 *		停止呼叫decode,但如果還有資料沒有讀取完,將含有剩餘資料的IoBuffer儲存到IoSession中,
			 *	當有新資料包來的時候再和新的合併後再呼叫decoder解碼
			 *		注意:沒有消費任何資料時不能返回true,否則會丟擲異常
			 */
			return false;
			
		}else{
			
			int length = buffer.getInt();
			
			MyMsg msg = new MyMsg();
			
			msg.setSender(buffer.getLong());
			msg.setReceiver(buffer.getLong());
			
			//	注意:20 = 訊息長度的位元組 + 傳送人和接收人的位元組
			msg.setContent(buffer.getString(length - 20, de));
			
			decoderOutput.write(msg);
			
			/**
			 *	CumulativeProtocolDecoder會再次呼叫decoder,並把剩餘的資料發下來繼續解碼
			 */
			return true;
		}
	}

}
    3、編解碼工廠

import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolEncoder;

/**
 *	編解碼工廠
 */
public class MyCoderFactory implements ProtocolCodecFactory{
	
	private MyDecoder decoder;
	private MyEncoder encoder;

	public MyCoderFactory() {
		this.decoder = new MyDecoder();
		this.encoder = new MyEncoder();
	}

	@Override
	public ProtocolDecoder getDecoder(IoSession session) throws Exception {
		return decoder;
	}

	@Override
	public ProtocolEncoder getEncoder(IoSession session) throws Exception {
		return encoder;
	}

}
三、伺服器端

    1、執行類

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class ServerMain {
	
	public static void main(String[] args) throws IOException {
		
		//	在伺服器端建立一個監聽連線的接收器 基於tcp/ip
		IoAcceptor acceptor = new NioSocketAcceptor();
		
		//	繫結的埠
		SocketAddress address = new InetSocketAddress("localhost", 8888);
		
		//	 獲取過濾器鏈
		DefaultIoFilterChainBuilder chain = acceptor.getFilterChain();
		
		//	新增日誌過濾器
		chain.addLast("logger", new LoggingFilter());
		
		// 配置自定義的編解碼器 
		chain.addLast("mycodec", new ProtocolCodecFilter(new MyCoderFactory()));
		
		//	新增資料處理的處理器
		acceptor.setHandler(new ServerHandler());
		
		//	進行配置資訊的設定     
        acceptor.getSessionConfig().setReadBufferSize(100);     
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); 
		
		//	繫結伺服器埠 
		acceptor.bind(address);
		
		System.out.println("伺服器開始在 8888 埠監聽.......");
		
	}
}
    2、Handler

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

public class ServerHandler extends IoHandlerAdapter{

	/**
	 *  接受訊息
	 */
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		MyMsg msg = (MyMsg) message;
		System.out.println("伺服器端接收到的訊息:" + msg);
		
		//	返回一個訊息給客戶端
		msg = new MyMsg(10000L, 10001L, "你好,這是來自伺服器的應答!");
		session.write(msg);
	}

	/**
	 *  傳送訊息
	 */
	public void messageSent(IoSession session, Object message) throws Exception {
		System.out.println("伺服器端開始傳送訊息...");
		super.messageSent(session, message);
	}

	/**
	 *  會話開啟
	 */
	public void sessionOpened(IoSession session) throws Exception {
		System.out.println("服務端會話已開啟...");
		super.sessionOpened(session);
	}

	/**
	 * 	異常處理
	 */
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		System.out.println("服務端發生異常了...");
		cause.printStackTrace();
	}

	/**
	 * 	會話關閉
	 */
	public void sessionClosed(IoSession session) throws Exception {
		System.out.println("服務端會話已關閉...");
		super.sessionClosed(session);
	}

	/**
	 * 	會話建立
	 */
	public void sessionCreated(IoSession session) throws Exception {
		System.out.println("服務端會話已建立...");
		super.sessionCreated(session);
	}

	/**
	 * 	連線空閒
	 */
	public void sessionIdle(IoSession session, IdleStatus status)
			throws Exception {
		System.out.println("服務端連線空閒中...");
		super.sessionIdle(session, status);
	}
}
四、客戶端

    1、連線類

import java.net.InetSocketAddress;
import java.net.SocketAddress;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

public class ClientMain {
	public static void main(String[] args) {
		
		//	建立客戶端聯結器 基於tcp/ip
		NioSocketConnector connector = new NioSocketConnector();
		
		//	連線的地址和埠
		SocketAddress address = new InetSocketAddress("localhost",8888);

		//	獲取過濾器鏈
		DefaultIoFilterChainBuilder chain = connector.getFilterChain();
	
		//	配置日誌過濾器和自定義編解碼器
		chain.addLast("logger", new LoggingFilter());
		chain.addLast("mycodec",new ProtocolCodecFilter(new MyCoderFactory()));
	
		//	新增處理器
		connector.setHandler(new ClientHandler());
		
		// 連線到伺服器 
		ConnectFuture future = connector.connect(address);
	
		//	等待連線建立完成
		future.awaitUninterruptibly();
		
		//	會話建立後傳送訊息到伺服器
		MyMsg msg = new MyMsg(10001L, 10000L, "你好,這是來自客戶端的請求!");
		future.getSession().write(msg);
	
		//	等待28000毫秒後連線斷開
		future.getSession().getCloseFuture().awaitUninterruptibly(28000);

		//	關閉連線
		connector.dispose();
	
	}
}
    2、Handler

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;

public class ClientHandler extends IoHandlerAdapter{

	/**
	 * 	接收訊息
	 */
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		System.out.println("客戶端接收到的訊息:" + (MyMsg)message);
	}

	/**
	 * 	傳送訊息
	 */
	public void messageSent(IoSession session, Object message) throws Exception {
		System.out.println("客戶端傳送訊息...");
		super.messageSent(session, message);
		
	}

	/**
	 * 	會話建立
	 */
	public void sessionOpened(IoSession session) throws Exception {
		
		System.out.println("客戶端已經連線到了伺服器...");
		
	}

	/**
	 * 	會話關閉
	 */
	public void sessionClosed(IoSession session) throws Exception {
		System.out.println("連線關閉...");
		super.sessionClosed(session);
	}
	
}

五、結果

    1、伺服器端

        

    2、客戶端

        

六、資源

    http://download.csdn.net/detail/u013379717/7574675

 

 

 

相關文章