1. 程式人生 > >Mina長連線框架實現Android客戶端與伺服器端通訊

Mina長連線框架實現Android客戶端與伺服器端通訊

一、概述

        Apache Mina Server 是一個網路通訊應用框架,也就是說,它主要是對基於TCP/IP、UDP/IP協議棧的通訊框架(當然,也可以提供JAVA 物件的序列化服務、虛擬機器管道通訊服務等),Mina 可以幫助我們快速開發高效能、高擴充套件性的網路通訊應用,Mina 提供了事件驅動、非同步(Mina 的非同步IO 預設使用的是JAVA NIO 作為底層支援)操作的程式設計模型。

二、伺服器端程式碼

MinaService.java

package com.czhappy.mina;

import java.net.InetSocketAddress;
import java.util.Date;

import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

public class MinaService {

	public static void main(String[] args) {
		IoAcceptor acceptor = new NioSocketAcceptor();
		//新增日誌過濾器
		acceptor.getFilterChain().addLast("logger", new LoggingFilter());
		acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
		acceptor.setHandler(new DemoServerHandler());
		acceptor.getSessionConfig().setReadBufferSize(2048);
		acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
		try {
			acceptor.bind(new InetSocketAddress(9226));
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("啟動服務");
	}
	
	/**  
	 * @ClassName: DemoServerHandler  
	 * @Description: 負責session物件的建立和監聽以及訊息的建立和接收監聽
	 * @author chenzheng
	 * @date 2016-12-9 下午3:57:11  
	 */
	private static class DemoServerHandler extends IoHandlerAdapter{
		
		//伺服器與客戶端建立連線
		@Override
		public void sessionCreated(IoSession session) throws Exception {
			System.out.println("伺服器與客戶端建立連線...");
			super.sessionCreated(session);
		}

		
		@Override
		public void sessionOpened(IoSession session) throws Exception {
			System.out.println("伺服器與客戶端連線開啟...");
			super.sessionOpened(session);
		}
		
		//訊息的接收處理
		@Override
		public void messageReceived(IoSession session, Object message)
				throws Exception {
			// TODO Auto-generated method stub
			super.messageReceived(session, message);
			String str = message.toString();
			Date date = new Date();
			session.write(date.toString());
			System.out.println("接收到的資料:"+str);
			
		}
		
		@Override
		public void messageSent(IoSession session, Object message)
				throws Exception {
			// TODO Auto-generated method stub
			super.messageSent(session, message);
		}
		
		@Override
		public void sessionClosed(IoSession session) throws Exception {
			// TODO Auto-generated method stub
			super.sessionClosed(session);
		}
	}
}

三、Android客戶端程式碼

ConnectionConfig.java

package com.czhappy.minaclient;

import android.content.Context;

/**
 * Description:構建者模式
 * User: chenzheng
 * Date: 2016/12/9 0009
 * Time: 16:37
 */
public class ConnectionConfig {

    private Context context;
    private String ip;
    private int port;
    private int readBufferSize;
    private long connectionTimeout;

    public Context getContext() {
        return context;
    }

    public String getIp() {
        return ip;
    }

    public int getPort() {
        return port;
    }

    public int getReadBufferSize() {
        return readBufferSize;
    }

    public long getConnectionTimeout() {
        return connectionTimeout;
    }

    public static class Builder{
        private Context context;
        private String ip = "192.168.168.20";
        private int port = 9226;
        private int readBufferSize = 10240;
        private long connectionTimeout = 10000;

        public Builder(Context context){
            this.context = context;
        }

        public Builder setIp(String ip){
            this.ip = ip;
            return this;
        }

        public Builder setPort(int port){
            this.port = port;
            return this;
        }

        public Builder setReadBufferSize(int readBufferSize){
            this.readBufferSize = readBufferSize;
            return this;
        }

        public Builder setConnectionTimeout(long connectionTimeout){
            this.connectionTimeout = connectionTimeout;
            return this;
        }

        private void applyConfig(ConnectionConfig config){

            config.context = this.context;
            config.ip = this.ip;
            config.port = this.port;
            config.readBufferSize = this.readBufferSize;
            config.connectionTimeout = this.connectionTimeout;
        }

        public ConnectionConfig builder(){
            ConnectionConfig config = new ConnectionConfig();
            applyConfig(config);
            return config;
        }
    }
}

ConnectionManager.java
package com.czhappy.minaclient;

import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import java.lang.ref.WeakReference;
import java.net.InetSocketAddress;

/**
 * Description:
 * User: chenzheng
 * Date: 2016/12/9 0009
 * Time: 16:21
 */
public class ConnectionManager {

    private static final String BROADCAST_ACTION = "com.commonlibrary.mina.broadcast";
    private static final String MESSAGE = "message";
    private ConnectionConfig mConfig;
    private WeakReference<Context> mContext;

    private NioSocketConnector mConnection;
    private IoSession mSession;
    private InetSocketAddress mAddress;

    public ConnectionManager(ConnectionConfig config){

        this.mConfig = config;
        this.mContext = new WeakReference<Context>(config.getContext());
        init();
    }

    private void init() {
        mAddress = new InetSocketAddress(mConfig.getIp(), mConfig.getPort());
        mConnection = new NioSocketConnector();
        mConnection.getSessionConfig().setReadBufferSize(mConfig.getReadBufferSize());
        mConnection.getFilterChain().addLast("logging", new LoggingFilter());
        mConnection.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
        mConnection.setHandler(new DefaultHandler(mContext.get()));
        mConnection.setDefaultRemoteAddress(mAddress);
    }

    /**
     * 與伺服器連線
     * @return
     */
    public boolean connnect(){
        Log.e("tag", "準備連線");
        try{
            ConnectFuture future = mConnection.connect();
            future.awaitUninterruptibly();
            mSession = future.getSession();

            SessionManager.getInstance().setSeesion(mSession);

        }catch (Exception e){
            e.printStackTrace();
            Log.e("tag", "連線失敗");
            return false;
        }

        return mSession == null ? false : true;
    }

    /**
     * 斷開連線
     */
    public void disContect(){
        mConnection.dispose();
        mConnection=null;
        mSession=null;
        mAddress=null;
        mContext = null;
        Log.e("tag", "斷開連線");
    }

    private static class DefaultHandler extends IoHandlerAdapter{

        private Context mContext;
        private DefaultHandler(Context context){
            this.mContext = context;

        }

        @Override
        public void sessionOpened(IoSession session) throws Exception {
            super.sessionOpened(session);
        }

        @Override
        public void messageReceived(IoSession session, Object message) throws Exception {
            Log.e("tag", "接收到伺服器端訊息:"+message.toString());
            if(mContext!=null){
                Intent intent = new Intent(BROADCAST_ACTION);
                intent.putExtra(MESSAGE, message.toString());
                LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
            }
        }
    }
}

MinaService.java
package com.czhappy.minaclient;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.HandlerThread;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * Description:
 * User: chenzheng
 * Date: 2016/12/9 0009
 * Time: 17:17
 */
public class MinaService extends Service{

    private ConnectionThread thread;


    @Override
    public void onCreate() {
        super.onCreate();
        thread = new ConnectionThread("mina", getApplicationContext());
        thread.start();
        Log.e("tag", "啟動執行緒嘗試連線");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        thread.disConnect();
        thread=null;

    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    class ConnectionThread extends HandlerThread{

        private Context context;
        boolean isConnection;
        ConnectionManager mManager;
        public ConnectionThread(String name, Context context){
            super(name);
            this.context = context;

            ConnectionConfig config = new ConnectionConfig.Builder(context)
                    .setIp("192.168.168.20")
                    .setPort(9226)
                    .setReadBufferSize(10240)
                    .setConnectionTimeout(10000).builder();

            mManager = new ConnectionManager(config);
        }

        @Override
        protected void onLooperPrepared() {
            while(true){
                isConnection = mManager.connnect();
                if(isConnection){
                    Log.e("tag", "連線成功");
                    break;
                }
                try {
                    Log.e("tag", "嘗試重新連線");
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        public void disConnect(){
            mManager.disContect();
        }
    }
}

SessionManager.java
package com.czhappy.minaclient;

import android.util.Log;

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

/**
 * Description:
 * User: chenzheng
 * Date: 2016/12/9 0009
 * Time: 17:50
 */
public class SessionManager {

    private static SessionManager mInstance=null;

    private IoSession mSession;
    public static SessionManager getInstance(){
        if(mInstance==null){
            synchronized (SessionManager.class){
                if(mInstance==null){
                    mInstance = new SessionManager();
                }
            }
        }
        return mInstance;
    }

    private SessionManager(){}

    public void setSeesion(IoSession session){
        this.mSession = session;
    }

    public void writeToServer(Object msg){
        if(mSession!=null){
            Log.e("tag", "客戶端準備傳送訊息");
            mSession.write(msg);
        }
    }

    public void closeSession(){
        if(mSession!=null){
            mSession.closeOnFlush();
        }
    }

    public void removeSession(){
        this.mSession=null;
    }
}

MinaTestActivity.java
package com.czhappy.minaclient;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

/**
 * Description:
 * User: chenzheng
 * Date: 2016/12/9 0009
 * Time: 18:01
 */
public class MinaTestActivity extends AppCompatActivity implements View.OnClickListener{

    private TextView start_service_tv, send_tv, receive_tv;

    private MessageBroadcastReceiver receiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_mina_test);

        initView();
        registerBroadcast();

    }

    private void registerBroadcast() {
        receiver = new MessageBroadcastReceiver();
        IntentFilter filter = new IntentFilter("com.commonlibrary.mina.broadcast");
        LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter);
    }

    private void initView() {
        receive_tv = (TextView) this.findViewById(R.id.receive_tv);
        start_service_tv = (TextView) this.findViewById(R.id.start_service_tv);
        start_service_tv.setOnClickListener(this);
        send_tv = (TextView) this.findViewById(R.id.send_tv);
        send_tv.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        switch(v.getId()){
            case R.id.start_service_tv:
                Log.e("tag", "點選啟動服務");
                Intent intent = new Intent(this, MinaService.class);
                startService(intent);
                break;
            case R.id.send_tv:
                Log.e("tag", "點擊發送訊息");
                SessionManager.getInstance().writeToServer("hello123");
                break;
        }
    }

    private void unregisterBroadcast(){
        LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopService(new Intent(this, MinaService.class));
        unregisterBroadcast();

    }

    private class MessageBroadcastReceiver extends BroadcastReceiver{
        @Override
        public void onReceive(Context context, Intent intent) {

            receive_tv.setText(intent.getStringExtra("message"));
        }
    }
}

四、執行結果

1.點選啟動服務,觀察客戶端是否與伺服器端連線成功;

2.點擊發送訊息,觀察訊息是否傳送成功,如果失敗判斷失敗在哪一步。

客戶端執行結果:


伺服器端執行結果:


五、原始碼下載