使用mina來開發socket程序

mina早於netty,出自同一人之手。個人感覺netty更棒但項目老大要求使用mina,所以就學習一下mina啦。學習的成果總結如下。

 

使用mina開發socket只需要IoAcceptor、IoHandlerAdapter、NioSocketConnector、ProtocolCodecFactory等幾個類基本上就可以進行開發了。

 

首先一個Server(簡單實例並非完整代碼) 負責Mina服務端的啓停

 

/**
 * @author shenbaise([email protected])
 * @date 2012-2-10
 * TODO Monitor Server2 將Mina交給spring管理,可以通過web界面來管理Mina的起停,參數設置等等
 */
@Controller("monitorServer2")
public class MonitorServer2 {
	
	private static final int PORT = Constant.remote_port; //定義監聽端口 
	
	private IoAcceptor acceptor = null;
	
	private InetSocketAddress socketAddres = null;
	
	@Autowired
	MonitorServerHandler monitorServerHandler;
	
	/**
	 * 啓動Server
	 * @throws IOException
	 */
	@RequestMapping("start")
	public void init() throws IOException{
		if(null == acceptor)				//如果 上一個Acceptor沒有被關閉,則新創建的Acceptor無法被綁定,同時上一個Acceptor將無法再被關閉。
		acceptor = new NioSocketAcceptor();		
		socketAddres = new InetSocketAddress(Constant.remote_address,PORT);
        // Add two filters : a logger and a codec
        acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
        acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName( "UTF-8" ))));
   
        // Attach the business logic to the server
//        acceptor.setHandler( monitorServerHandler );
        acceptor.setHandler(new MonitorServerHandler());
        // Configurate the buffer size and the iddle time
        acceptor.getSessionConfig().setReadBufferSize( 2048 );
        acceptor.getSessionConfig().setIdleTime( IdleStatus.BOTH_IDLE, 10 );
        acceptor.getSessionConfig().setUseReadOperation(true); 
        // And bind !
        acceptor.bind(socketAddres);
	}
	/**
	 * 銷燬並退出
	 */
	@RequestMapping("stop")
	public void destroy(){
		if (null!=acceptor) {
			acceptor.unbind(socketAddres);
			acceptor.getFilterChain().clear();	// 清空Filter chain,防止下次重新啓動時出現重名錯誤
			acceptor.dispose();					// 可以另寫一個類存儲IoAccept,通過spring來創建,這樣調用dispose後也會重新創建一個新的。或者可以在init方法內部進行創建。
			acceptor = null;
			//		System.exit(0);		將導致容器停止
		}
		
	}
	
    public static void main(String[] args) throws IOException {
    	MonitorServer2 server = new MonitorServer2();
    	server.init();
    	
    	try {
			Thread.sleep(600000);
			
			server.destroy();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
	
}
 

一個對應的Handler,負責業務邏輯處理

 

@Service("monitorServerHandler")
public class MonitorServerHandler extends IoHandlerAdapter {
	
	@SuppressWarnings("unused")
	@Autowired
	private MCacheClient cache;
	
	private int count = 0;
	
	public static final String ALERT_MSG_CACHE_KEY = "1457261687388459164";
	
	@Override
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		cause.printStackTrace();
		super.exceptionCaught(session, cause);
	}
	
	/**
	 * 將接收到的消息存庫或者緩存
	 */
	
	@Override
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		/*服務端的邏輯一般都在這裏寫*/

	}

	@Override
	public void messageSent(IoSession session, Object message) throws Exception {
		System.out.println("SERVER=>messageSent:" + (String)message);
		super.messageSent(session, message);
	}

	@Override
	public void sessionClosed(IoSession session) throws Exception {
		System.out.println("SERVER=>sessionClosed: current sessionId:"+session.getId());
		super.sessionClosed(session);
	}

	@Override
	public void sessionCreated(IoSession session) throws Exception {
		System.out.println("SERVER=>sessionCreated: current sessionId:"+session.getId());
		super.sessionCreated(session);
	}

	@Override
	public void sessionIdle(IoSession session, IdleStatus status)
			throws Exception {
		System.out.println("SERVER=>sessionIdle:" + session.getIdleCount( status ));
		super.sessionIdle(session, status);
	}

	@Override
	public void sessionOpened(IoSession session) throws Exception {
		System.out.println("SERVER=>sessionOpened: current sessionId:"+session.getId());
		super.sessionOpened(session);
	}
	
}
 

對應Client端也是類似的,一個類負責連接Server,一個來負責業務邏輯的處理。

 

Client端:

 

public class MonitorClient {
	
	private static final int PORT = 10000;
	public  static String address = "127.0.0.1";
	private static InetSocketAddress socketAddres = new InetSocketAddress(address,PORT);
	
	private NioSocketConnector connector = null; 
	
	/**
	 * 啓動(在listener中啓動是需要新建一個線程來連接Server,否則web容器會阻塞而無法啓動。)
	 */
	public void init(){
		connector = new NioSocketConnector();
		connector.getFilterChain().addLast( "logger", new LoggingFilter() ); 
		connector.getFilterChain().addLast( "codec", new ProtocolCodecFilter( new MyCodeFactory( Charset.forName( "UTF-8" )))); //設置編碼過濾器 
		connector.setConnectTimeoutMillis(3000); 
		connector.setHandler(new MonitorClientHandler());//設置事件處理器 
		ConnectFuture cf = connector.connect(socketAddres);//建立連接 
		
		cf.awaitUninterruptibly();

		cf.getSession().getCloseFuture().awaitUninterruptibly();//等待連接斷開 
		
	}
	
	/**
	 * 銷燬
	 */
	public void destroy(){
		socketAddres = null;
		connector.dispose();
	}
	
	
	public static void main(String[] args) { 
//		MonitorClient client = new MonitorClient();
//		client.init();
//		try {
//			Thread.sleep(70000);
//			
//			client.destroy();
//		} catch (InterruptedException e) {
//			e.printStackTrace();
//		}
		
		ClientThread thread = new ClientThread();
		Thread cThread = new Thread(thread);
		cThread.start();
	}
	/**
	 * 啓動(在新建線程中連接Server)
	 */
	public void init2(){
		ClientThread thread = new ClientThread();
		Thread cThread = new Thread(thread);
		cThread.start();
	}
	
}

 Client端的Handler:

 

public class MonitorClientHandler extends IoHandlerAdapter {
	
	int count = 0;
	StringBuilder sb = new StringBuilder();
	int pos = 0;
//	@Autowired BaseDao baseDao;
	
	@Override
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		cause.printStackTrace();
		super.exceptionCaught(session, cause);
	}

	@Override
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		/*Client端的邏輯會在這裏*/
		super.messageReceived(session, message);
	}

	@Override
	public void messageSent(IoSession session, Object message) throws Exception {
                 /*或者會在這裏,當然每個地方都會有一些東西需要處理,例如創建、關閉、空閒等等*/
		super.messageSent(session, message);
	}

	@Override
	public void sessionClosed(IoSession session) throws Exception {
		/*這裏可以實現重連,但是這會涉及到你代碼的一些資源分配或調度邏輯,跑一個線程進行重連*/
		super.sessionClosed(session);
	}

	@Override
	public void sessionCreated(IoSession session) throws Exception {
		System.out.println("CLIENT=>sessionCreated: current sessionId:"+session.getId());
		super.sessionCreated(session);
	}

	@Override
	public void sessionIdle(IoSession session, IdleStatus status)
			throws Exception {
         
		System.out.println("CLIENT=>sessionIdle:" + session.getIdleCount( status ));
		super.sessionIdle(session, status);
	}

	@Override
	public void sessionOpened(IoSession session) throws Exception {
		System.out.println("CLIENT=>sessionOpened: current sessionId:"+session.getId());
		super.sessionOpened(session);
	}
	
}

 最後還有ProtocolCodecFactory也是比較重要的

一個很簡單的實現如下:

 

public  class MyCodeFactory implements ProtocolCodecFactory {
		
		private LineDelimiter enLineDelimiter = new LineDelimiter(Constant.CHAR2);
	
	    private final TextLineEncoder encoder;
	    private final TextLineDecoder decoder;
	    /*final static char endchar = 0x1a;*/
	    final static String endchar = Constant.CHAR2;
	    
	    public MyCodeFactory() {
	        this(Charset.forName("gb2312"));
	    }
	    
	    public MyCodeFactory(Charset charset) {
	    	 encoder = new TextLineEncoder(charset, enLineDelimiter);
	         decoder = new TextLineDecoder(charset, enLineDelimiter);
	         
	         }

		public ProtocolDecoder getDecoder(IoSession session) throws Exception {
			// TODO Auto-generated method stub
			return decoder;
		}
		public ProtocolEncoder getEncoder(IoSession session) throws Exception {
			// TODO Auto-generated method stub
			return encoder;
		}
		public int getEncoderMaxLineLength() {
	        return encoder.getMaxLineLength();
	    }
	    public void setEncoderMaxLineLength(int maxLineLength) {
	        encoder.setMaxLineLength(maxLineLength);
	    }
	    public int getDecoderMaxLineLength() {
	        return decoder.getMaxLineLength();
	    }
	    public void setDecoderMaxLineLength(int maxLineLength) {
	        decoder.setMaxLineLength(maxLineLength);
	    }

}

 mina的TextLineCodecFactory默認以換行來進行分隔,我們也可以自定義。以TextLineCodecFactory爲例,mina在發送是會在發送內容之後自動加一個換行符,在接收時會按換行符來截取收到的內容。

 

在web工程中啓動mina客戶端時應該新開一個線程,不要用主線程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章