Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android MINA框架之實戰總結(一) Mina連接,斷開,重連

Android MINA框架之實戰總結(一) Mina連接,斷開,重連

編輯:關於Android編程

(一). 前言

Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 組織一個較新的項目,它為開發高性能和高可用性的網絡應用程序提供了非常便利的框架。當前發行的 MINA 版本支持基於 Java NIO 技術的 TCP/UDP 應用程序開發、串口通訊程序(只在最新的預覽版中提供),MINA 所支持的功能也在進一步的擴展中。
總之:我們簡單理解它是一個封裝底層IO操作,提供高級操作API的通訊框架!

(二). MINA體系結構

Mina鳥瞰圖

這裡寫圖片描述
Mina位於用戶程序和網絡處理之間,將用戶從復雜的網絡處理中解耦,我們就可以更加關注業務領域。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMyBpZD0="minamina組件結構圖">Minamina組件結構圖

這裡寫圖片描述
Mina框架被分成了主要的3個組件部分:

?I/O Service,具體提供服務的組件。
?I/O Filter Chain,過濾器鏈,用於支持各種切面服務。
?I/O Handler,用於處理用戶的業務邏輯。

相對應的,為了創建一個基於Mina的應用程序,我們需要:

?創建I/O Service :可選擇Mina提供的Services如(*Acceptor)或實現自己的Service。
?創建I/O Filter :同樣可以選擇Mina提供的各類filter,也可以實現自己的編解碼過濾器等。
?實現I/O Handler,實現Handler接口,處理各種消息。

Mina服務端結構

這裡寫圖片描述
服務端的作用就是開啟監聽端口,等待請求的到來、處理他們、以及將發送對請求的響應。同時,服務端會為每個連接創建session,在session周期內提供各種精度的服務,比如連接創建時(sessionCreated(IoSession session))、連接等待時(sessionIdle(IoSession session, IdleStatus status))、連接銷毀時(sessionClosed(IoSession session))等。mina的api為TCP/UDP提供的一致性Server端操作。

?IOAcceptor 監聽來自網絡的請求。
?當新的連接建立時,一個新的session會被創建,該session用作對同一IP/端口組合的客戶端提供服務。
?數據包需經過一系列的過濾器,這些過濾器可用來修改數據包的內容(如轉換對象、添加或修改信息等),其中將原始字節流轉換成POJO對象是非常有用的。當然這需要解編碼器提供支持。
?最後這些數據包或轉化後的對象將交由IOHandler處理,我們將實現IOHandler用於處理具體的業務邏輯。

Mina客戶端結構

這裡寫圖片描述
客戶端需要連接服務端,發送請求並處理響應。實際上客戶端的結構和服務端極其相似。

?客戶端首先需要創建IOConnector對象,綁定服務端的IP和端口。
?一旦連接成功,一個於本次連接綁定的session對象將被創建。
?客戶端發送給服務端的請求都需要經過一系列的fliter。
?同樣,響應消息的接受也會經過一系列的filter再到IOHandler被處理。

所以整體上,mina提供良好的一致性調用和封裝結構。在使用mina創建基於網絡的程序應用時,投入的學習成本比較低。

(三). 參考資料

本篇文章主要參考以下MINA資料:
1.Apache-mina學習筆記,非常全都資料,附帶大量實例:http://blog.csdn.net/cgwcgw_/article/details/18402769(比較完善的學習MINA的筆記)
2.Mina 斷線重連:http://chwshuang.iteye.com/blog/2028951(注:講解了斷線重連的原理)
3.mina學習筆記二:從官方例子開始 :http://blog.csdn.net/yoara/article/details/37324821(注:有mina體系結構圖,能對MINA有個清晰的了解)

(四). 下載資料

本篇文章的DEMO下載地址:
1.MINA服務端:https://github.com/wsm2015/CWMinaServer
2.MINA客戶端:https://github.com/wsm2015/CWMinaClient

(五). 服務器搭建

第一步.下載使用的Jar包:

登錄http://mina.apache.org/downloads.html下載最新 mina壓縮包(我下的是apache-mina-2.0.13-bin.zip),解壓獲得mina-core-2.0.13.jar和slf4j-api-1.7.14.jar(注:slf4j-api-1.7.14.jar文件在apache-mina-2.0.13-bin.zip\apache-mina-2.0.13\lib目錄下)

第二步.服務端程序

創建一個簡單的服務端程序:(服務端綁定3344端口)

public class DemoServer {
    // 端口號,要求客戶端與服務器端一致
    private static int PORT = 3344;

    public static void main(String[] args) {
        IoAcceptor acceptor = null;
        try {
            // 創建一個非阻塞的server端的Socket
            acceptor = new NioSocketAcceptor();
            // 設置過濾器(使用mina提供的文本換行符編解碼器)
            acceptor.getFilterChain().addLast("codec",
                    new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"),
                            LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue())));
            // 自定義的編解碼器
            // acceptor.getFilterChain().addLast("codec", new
            // ProtocolCodecFilter(new CharsetCodecFactory()));
            // 設置讀取數據的換從區大小
            acceptor.getSessionConfig().setReadBufferSize(2048);
            // 讀寫通道10秒內無操作進入空閒狀態
            acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);
            // 為接收器設置管理服務
            acceptor.setHandler(new DemoServerHandler());
            // 綁定端口
            acceptor.bind(new InetSocketAddress(PORT));
            System.out.println("服務器啟動成功... 端口號未:" + PORT);

        } catch (Exception e) {
            System.out.println("服務器啟動異常...");
            e.printStackTrace();
        }

    }

}



package com.changwu;

import java.util.Date;

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

public class DemoServerHandler extends IoHandlerAdapter {
    // 從端口接受消息,會響應此方法來對消息進行處理
    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        super.messageReceived(session, message);
        String msg = message.toString();
        if ("exit".equals(msg)) {
            // 如果客戶端發來exit,則關閉該連接
            session.close(true);
        }
        // 向客戶端發送消息
        Date date = new Date();
        session.write(date);
        System.out.println("服務器接受消息成功..." + msg);
    }

    // 向客服端發送消息後會調用此方法
    @Override
    public void messageSent(IoSession session, Object message) throws Exception {
        super.messageSent(session, message);
//      session.close(true);//加上這句話實現短連接的效果,向客戶端成功發送數據後斷開連接
        System.out.println("服務器發送消息成功...");
    }

    // 關閉與客戶端的連接時會調用此方法
    @Override
    public void sessionClosed(IoSession session) throws Exception {
        super.sessionClosed(session);
        System.out.println("服務器與客戶端斷開連接...");
    }

    // 服務器與客戶端創建連接
    @Override
    public void sessionCreated(IoSession session) throws Exception {
        super.sessionCreated(session);
        System.out.println("服務器與客戶端創建連接...");
    }

    // 服務器與客戶端連接打開
    @Override
    public void sessionOpened(IoSession session) throws Exception {
        System.out.println("服務器與客戶端連接打開...");
        super.sessionOpened(session);
    }

    @Override
    public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
        super.sessionIdle(session, status);
        System.out.println("服務器進入空閒狀態...");
    }

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        super.exceptionCaught(session, cause);
        System.out.println("服務器發送異常...");
    }

}

第三步.運行DemoServer.java文件,成功後的效果圖如下:

(六). Android客戶端

第一步.導入下載後的客戶端項目

MINA客戶端項目結構圖

第二步.客戶端程序

public class MainActivity extends Activity {

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

    public void click(View v) {
        MinaThread mThread = new MinaThread();
        mThread.start();
    }
}

public class MinaThread extends Thread {

    private IoSession session = null;
    private IoConnector connector = null;

    @Override
    public void run() {
        super.run();
        // TODO Auto-generated method stub]
        System.out.println("客戶端鏈接開始...");
        connector = new NioSocketConnector();
        System.out.println(101);
        // 設置鏈接超時時間
        connector.setConnectTimeoutMillis(10000);
        System.out.println(102);
        // 添加過濾器
        // connector.getFilterChain().addLast("codec", new
        // ProtocolCodecFilter(new CharsetCodecFactory()));
        connector.getFilterChain().addLast("codec",
                new ProtocolCodecFilter(new TextLineCodecFactory(Charset.forName("UTF-8"),
                        LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue())));
        System.out.println(110);
        connector.setHandler(new MinaClientHandler());
        System.out.println(111);
        connector.setDefaultRemoteAddress(new InetSocketAddress(ConstantUtil.OUT_MATCH_PATH, ConstantUtil.WEB_MATCH_PORT));
        // 監聽客戶端是否斷線
        connector.addListener(new IoListener() {
            @Override
            public void sessionDestroyed(IoSession arg0) throws Exception {
                // TODO Auto-generated method stub
                super.sessionDestroyed(arg0);
                try {
                    int failCount = 0;
                    while (true) {
                        Thread.sleep(5000);
                        System.out.println(((InetSocketAddress) connector.getDefaultRemoteAddress()).getAddress()
                                .getHostAddress());
                        ConnectFuture future = connector.connect();
                        System.out.println("斷線2");
                        future.awaitUninterruptibly();// 等待連接創建完成
                        System.out.println("斷線3");
                        session = future.getSession();// 獲得session
                        System.out.println("斷線4");
                        if (session != null && session.isConnected()) {
                            System.out.println("斷線5");
                            System.out.println("斷線重連["
                                    + ((InetSocketAddress) session.getRemoteAddress()).getAddress().getHostAddress()
                                    + ":" + ((InetSocketAddress) session.getRemoteAddress()).getPort() + "]成功");
                            session.write("start");
                            break;
                        } else {
                            System.out.println("斷線重連失敗---->" + failCount + "次");
                        }
                    }
                } catch (Exception e) {
                    // TODO: handle exception
                }
            }
        });
        //開始連接
        try {
            System.out.println(112);
            ConnectFuture future = connector.connect();
            System.out.println(113);
            future.awaitUninterruptibly();// 等待連接創建完成
            System.out.println(114);
            session = future.getSession();// 獲得session
            System.out.println(115);
            if (session != null && session.isConnected()) {
                session.write("start");
            } else {
                System.out.println("寫數據失敗");
            }

            System.out.println(11);
        } catch (Exception e) {
            System.out.println("客戶端鏈接異常...");
        }
        System.out.println(118);
        if (session != null && session.isConnected()) {
            session.getCloseFuture().awaitUninterruptibly();// 等待連接斷開
            System.out.println("客戶端斷開111111...");
            // connector.dispose();//徹底釋放Session,退出程序時調用不需要重連的可以調用這句話,也就是短連接不需要重連。長連接不要調用這句話,注釋掉就OK。
        }

    }

}

public class MinaClientHandler extends IoHandlerAdapter {

    @Override
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
        Log.i("TEST", "客戶端發生異常");
        super.exceptionCaught(session, cause);
    }

    @Override
    public void messageReceived(IoSession session, Object message) throws Exception {
        String msg = message.toString();
        Log.i("TEST", "i客戶端接收到的信息為:" + msg);
        super.messageReceived(session, message);
    }

    @Override
    public void messageSent(IoSession session, Object message) throws Exception {
        // TODO Auto-generated method stub
        super.messageSent(session, message);
    }
}


public class IoListener implements IoServiceListener{

    @Override
    public void serviceActivated(IoService arg0) throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    public void serviceDeactivated(IoService arg0) throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    public void serviceIdle(IoService arg0, IdleStatus arg1) throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    public void sessionClosed(IoSession arg0) throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    public void sessionCreated(IoSession arg0) throws Exception {
        // TODO Auto-generated method stub

    }

    @Override
    public void sessionDestroyed(IoSession arg0) throws Exception {
        // TODO Auto-generated method stub

    }

}

public class ConstantUtil {
    /** 本地局域網IP地址 **/
    public final static String  WEB_MATCH_PATH="192.168.1.102";
    /** 用花生殼轉換本地局域網後的IP地址,可供外網訪問 **/
    public final static String  OUT_MATCH_PATH="15zr163427.iask.in";
    /** 用花生殼轉換本地局域網後的端口號 **/
    public final static int WEB_MATCH_PORT=25400;
}

(七). 連接

這裡寫圖片描述
這裡直接調用了

ConnectFuture future = connector.connect();
future.awaitUninterruptibly();// 等待連接創建完成

,在前面已經設置了默認服務器

//設置默認連接遠程服務器的IP地址和端口

connector.setDefaultRemoteAddress(new InetSocketAddress(ConstantUtil.OUT_MATCH_PATH, ConstantUtil.WEB_MATCH_PORT));

(八). 斷開

在服務端DemoServerHandler.java執行session.close(true);如圖所示:
這裡寫圖片描述

(九). 斷線重連

在客戶端MinaThread.java中給connector添加監聽Session關閉事件;

注意:因為我們在在服務端DemoServerHandler.java中messageSent()方法下執行了session.close(true);所以我們會不斷點開和連接服務器;成功後的效果圖如下:
這裡寫圖片描述

(十). 長連接和短連接

Mina本身的效果就是長連接,與長連接相對應的是短連接,比如常說的請求/響應模式(HTTP協議就是典型的請求/響應模式)—–客戶端向服務端發送一個請求,建立連接後,服務端處理並響應成功,此時就主動斷開連接了!

短連接是一個簡單而有效的處理方式,也是應用最廣的。Mina是Java NIO實現的應用框架,更傾向於短連接的服務;問題是哪一方先斷開連接呢?可以在服務端,也可以在客戶端,但是提倡在服務端主動斷開;

Mina的服務端業務邏輯處理類中有一個方法messageSent,他是在服務端發送信息成功後調用的:

@Override
    public void messageSent(IoSession session, Object message) throws Exception {
        super.messageSent(session, message);
        System.out.println("服務器發送消息成功...");
    }

修改後為

@Override
    public void messageSent(IoSession session, Object message) throws Exception {
        super.messageSent(session, message);
        session.close(true);//加上這句話實現短連接的效果,向客戶端成功發送數據後斷開連接
        System.out.println("服務器發送消息成功...");
    }

這時候客戶端與服務端就是典型的短連接了;再次測試,會發現客戶端發送請求,接收成功後就自動關閉了,進程只剩下服務端了!

到此為止,我們已經可以運行一個完整的基於TCP/IP協議的應用程序啦!

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved