一.mina简介

Apache Mina是一个能够帮助用户开发高性能和高伸缩性网络应用程序的框架。与Netty出自同一人之手,都是一个介于应用程序与网络之间的NIO框架,通过Java nio技术基于TCP/IP和UDP/IP协议提供了抽象的、事件驱动的、异步的API,使程序员从繁琐的网络操作中解脱出来,花更多的时间在业务处理上。

mina分为三层,如下图:

1、IOService层:处理IO操作

2、IOFilter层:过滤器链,日志处理、字节变换、对象转换等操作

3、IOHandler层:真正的处理业务逻辑的地方

Android socktet 长连接_Android socktet 长连接

mina核心类

IoService

IoService用来管理各种IO服务,在mina中,这些服务可以包括session、filter、handler等

Android socktet 长连接_java_02


上面的图简单介绍了IoService的职责,以及其具体实现类AbstractIoService中的职责。在比较大的框架中,都是采用了大量的抽象类之间继承,采用层级实现细节这样的方式来组织代码。所以在mina中看到Abstract开头的类,并不仅仅只是一个抽象,其实里面也包含很多的实现了。

服务端IoAcceptor及相关类

IOAcceptor相当于是对ServerSocketChannel的封装,最重要的两个操作是绑定与接受连接。

Android socktet 长连接_ide_03


Acceptor线程专门负责接受连接,在其上有一个selector,轮询是否有连接建立上来,当有连接建立上来,调用ServerSocketChannel.accept方法来接受连接,这个方法返回一个session对象,然后将这个session对象加入processor中,由processor遍历每个session来完成真正的IO操作。processor上也有一个selector与一个Processor线程,selector用于轮询session,Processor线程处理每个session的IO操作。

客户端IOConnector及相关类

Android socktet 长连接_sed_04


IOConnector的设计与IOAcceptor几乎完全一样,唯一不同的是与Acceptor线程对应的是Connector线程,在完成连接操作后也是扔了一个session对象到Processor中。

过滤器(Filter)

下面是官网提供的过滤器

Android socktet 长连接_sed_05


可以通过继承IoFilterAdapter来实现自己的过滤器,但一般不需要这么做,以下是一些常用的过滤器:

  • LoggingFilter 记录mina所有日志
  • ProtocolCodecFilter 协议编码解码过滤器
  • CompressionFilter 数据压缩过滤器
  • SSLFilter 数据加密过滤器
IoSession

Mina每建立一个连接同时会创建一个session对象,用于保存这次读写需要用到的所有信息。从抽象类AbstractIoSession中可以看出session具有如下功能:
1、从attributes成员可以看出session可以存放用户关心的键值对
2、注意到WriteRequestQueue,这是一个写请求队列,processor中调用flush或者flushNow方法时会将用户写入的数据包装成一个writeRequest对象,并加入这个队列中。
3、提供了大量的统计功能,比如接收到了多少消息、最后读取时间等
在代码中设置session:

// 创建服务器监听  
IoAcceptor acceptor = new NioSocketAcceptor();  
// 设置buffer的长度  
acceptor.getSessionConfig().setReadBufferSize(2048);  
// 设置连接超时时间  
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);

连接到来创建一个session,初始化好之后加入到processor负责的一个队列中。processor线程会把队列中的session对应的通道都注册到它自己的selector上,然后这个selector轮询这些通道是否准备就绪,一旦准备就绪就调用对应方法进行处理(read or flushNow)。

Mina中的session具有状态,且状态之间是可以相互转化的

Android socktet 长连接_Android socktet 长连接_06


IoFilter与IoHandler就是在这些状态上面加以干预,下面重点看一下IDLE状态,它分三种:

Idle for read:在规定时间内没有数据可读

Idle for write:在规定时间内没有数据可写

Idle for both:在规定时间内没有数据可读和可写

这三种状态分别对应IdleStatus类的三个常量:READER_IDLE、WRITER_IDLE、BOTH_IDLE

前面session的用法中有如下设置:

acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);

二.保持长连接

服务端

引入相关jar包
(1)mina-core-2.0.16.jar
(2)slf4j-api-1.7.21.jar及相关jar包

  • MainService.java
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 DemoServiceHandler());
        acceptor.getSessionConfig().setMaxReadBufferSize(2048);
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); //10秒没有读写就进入空闲状态
        try {
            acceptor.bind(new InetSocketAddress(9123));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

   private static class DemoServiceHandler extends IoHandlerAdapter{

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

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

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

        @Override
        public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
            super.exceptionCaught(session, cause);
        }

        @Override
        public void messageReceived(IoSession session, Object message) throws Exception {
            super.messageReceived(session, message);
            String msg = message.toString();
            session.write(new Date());
            System.out.println("接收到的数据:"+msg);

        }

        @Override
        public void messageSent(IoSession session, Object message) throws Exception {
            super.messageSent(session, message);
        }
    }
}
客户端

相关jar包
(1)mina-core-2.0.16.jar
(2)slf4j-android-1.6.1-RC1.jar

  • ConnectionManager.java
public class ConnectionManager {
    private static final String BROADCAST_ACTION="com.commonlibrary.mina";
    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("logger", new LoggingFilter());
        mConnection.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
        mConnection.setHandler(new DefaultHandler(mContext.get()));
        mConnection.setDefaultRemoteAddress(mAddress);
    }

    public boolean connect() {
        try {
            ConnectFuture future = mConnection.connect();
            future.awaitUninterruptibly();
            mSession = future.getSession();
            SessionManager.getInstance().setSeesion(mSession);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return  mSession != null ? true:false;

    }
    public void disConnection()
    {
        mConnection.dispose();
        mConnection = null;
        mSession = null;
        mAddress = null;
        mContext = null;
    }

    private static class DefaultHandler extends IoHandlerAdapter {
        private final Context mContext;


        public DefaultHandler(Context context) {
            this.mContext = context;
        }

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

        @Override
        public void sessionOpened(IoSession session) throws Exception {
            super.sessionOpened(session);
            //将我们的session保存到我们的session manager类中, 从而可以发送消息到服务器
        }

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

        @Override
        public void messageReceived(IoSession session, Object message) throws Exception {
            super.messageReceived(session, message);
            if (mContext != null)
            {
                Intent intent = new Intent(BROADCAST_ACTION);
                intent.putExtra(MESSAGE, message.toString());
                LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
            }
        }

        @Override
        public void messageSent(IoSession session, Object message) throws Exception {
            super.messageSent(session, message);

        }
    }
}
  • ConnectionConfig.java
public class ConnectionConfig {
    private Context context;
    private String ip;
    private int port;
    private int readBufferSize;
    private long connectionTimeout;

    public Context getContext() {
        return context;
    }

    public void setContext(Context context) {
        this.context = context;
    }

    public String getIp() {
        return ip;
    }

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

    public int getPort() {
        return port;
    }

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

    public int getReadBufferSize() {
        return readBufferSize;
    }

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

    public long getConnectionTimeout() {
        return connectionTimeout;
    }

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

    //构建者模式
    public static class Builder{
        private Context context;
        private String ip="10.90.24.139";
        private int port=9123;
        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 = readBufferSize;
            config.connectionTimeout = this.connectionTimeout;
        }
        public ConnectionConfig builder()
        {
            ConnectionConfig config = new ConnectionConfig();
            applyConfig(config);
            return config;
        }
    }

}
  • SessionManager.java
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;
    }

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

    public SessionManager(IoSession mSession) {
        this.mSession = mSession;
    }

    /**
     * 将对象写到服务端
     * @param msg
     */
    public void writeToServer(Object msg)
    {
        if (mSession != null) {
            mSession.write(msg);
        }
    }

    public void closeSession()
    {
        if (mSession != null)
            mSession.closeOnFlush();
    }
    public void removeSession()
    {
        this.mSession = null;
    }
}
  • MinaActivity.java
public class MinaActivity extends Activity implements View.OnClickListener{
    private MessageBroadcastReceiver receiver = new MessageBroadcastReceiver();
    private Button btn1, btn2;
    private TextView message;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.mina_test);
        message = (TextView) findViewById(R.id.message);
        btn1 = (Button) findViewById(R.id.btn1);
        btn2 = (Button) findViewById(R.id.btn2);
        btn1.setOnClickListener(this);
        btn2.setOnClickListener(this);
        registerBroadcast();
    }

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

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

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

    @Override
    public void onClick(View v) {
        switch (v.getId())
        {
            case R.id.btn1:
                SessionManager.getInstance().writeToServer("123");
                break;
            case R.id.btn2:
                Intent intent = new Intent(this, MinaService.class);
                startService(intent);
                break;
        }
    }


    private class MessageBroadcastReceiver extends BroadcastReceiver
    {

        @Override
        public void onReceive(Context context, Intent intent) {
            message.setText(intent.getStringExtra("message"));
        }
    }
}

布局文件中就是两个按钮和一个文本控件,代码就不贴了。

  • MinaService.java
public class MinaService extends Service {

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

    @Override
    public void onCreate() {
        super.onCreate();
        thread = new ConnectionHandlerThread("mina", getApplicationContext());
        System.out.println("service create:");
        thread.start();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        thread.disConnection();
    }

    /**
     * 负责调用ConnectionManager
     */
    class ConnectionHandlerThread extends HandlerThread {
        private Context context;
        boolean isConnection;
        ConnectionManager mManager;

        public ConnectionHandlerThread(String name, Context context) {
            super(name);
            this.context = context;
            ConnectionConfig config = new ConnectionConfig.Builder(context)
                    .setIp("10.90.24.139").setPort(9123)
                    .setReadBufferSize(10240).setReadBufferSize(10000).builder();
            System.out.println(config.getReadBufferSize());
            mManager = new ConnectionManager(config);
        }

        @Override
        protected void onLooperPrepared() {
            super.onLooperPrepared();
            while (true) {
                isConnection = mManager.connect(); //

                if (isConnection) {
                    break;
                }
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }

        public void disConnection() {
            mManager.disConnection();
        }
    }

}

注意:

(1)局部广播的使用(LocalBroadcastManager)

(2)android中AlertDialog使用的构建者模式

(3)HandlerThread的使用