mina保持android端\服务端的长连接-copy

一.mina简介

Apache Mina是一个能够帮助用户开发高性能和高伸缩性网络应用程序的框架。与Netty出自同一人之手,都是一个介于应用程序与网络之间的NIO框架,通过Java nio技术基于TCP/IP和UDP/IP协议提供了抽象的、事件驱动的、异步的API,使程序员从繁琐的网络操作中解脱出来,花更多的时间在业务处理上。
mina分为三层,如下图:
1、IOService层:处理IO操作
2、IOFilter层:过滤器链,日志处理、字节变换、对象转换等操作
3、IOHandler层:真正的处理业务逻辑的地方
这里写图片描述

mina核心类

IoService

IoService用来管理各种IO服务,在mina中,这些服务可以包括session、filter、handler等
这里写图片描述
上面的图简单介绍了IoService的职责,以及其具体实现类AbstractIoService中的职责。在比较大的框架中,都是采用了大量的抽象类之间继承,采用层级实现细节这样的方式来组织代码。所以在mina中看到Abstract开头的类,并不仅仅只是一个抽象,其实里面也包含很多的实现了。

服务端IoAcceptor及相关类

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

客户端IOConnector及相关类

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

过滤器(Filter)

下面是官网提供的过滤器
这里写图片描述
可以通过继承IoFilterAdapter来实现自己的过滤器,但一般不需要这么做,以下是一些常用的过滤器:

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

IoSession

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

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

连接到来创建一个session,初始化好之后加入到processor负责的一个队列中。processor线程会把队列中的session对应的通道都注册到它自己的selector上,然后这个selector轮询这些通道是否准备就绪,一旦准备就绪就调用对应方法进行处理(read or flushNow)。
Mina中的session具有状态,且状态之间是可以相互转化的
这里写图片描述
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
  1.  
    public class MinaService {
  2.  
    public static void main(String[] args) {
  3.  
    IoAcceptor acceptor = new NioSocketAcceptor();
  4.  
    acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
  5.  
    acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
  6.  
    acceptor.setHandler(new DemoServiceHandler());
  7.  
    acceptor.getSessionConfig().setMaxReadBufferSize(2048);
  8.  
    acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); //10秒没有读写就进入空闲状态
  9.  
    try {
  10.  
    acceptor.bind(new InetSocketAddress(9123));
  11.  
    } catch (IOException e) {
  12.  
    e.printStackTrace();
  13.  
    }
  14.  
    }
  15.  
     
  16.  
    private static class DemoServiceHandler extends IoHandlerAdapter{
  17.  
     
  18.  
    @Override
  19.  
    public void sessionCreated(IoSession session) throws Exception {
  20.  
    super.sessionCreated(session);
  21.  
    }
  22.  
     
  23.  
    @Override
  24.  
    public void sessionOpened(IoSession session) throws Exception {
  25.  
    super.sessionOpened(session);
  26.  
    }
  27.  
     
  28.  
    @Override
  29.  
    public void sessionClosed(IoSession session) throws Exception {
  30.  
    super.sessionClosed(session);
  31.  
    }
  32.  
     
  33.  
    @Override
  34.  
    public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
  35.  
    super.exceptionCaught(session, cause);
  36.  
    }
  37.  
     
  38.  
    @Override
  39.  
    public void messageReceived(IoSession session, Object message) throws Exception {
  40.  
    super.messageReceived(session, message);
  41.  
    String msg = message.toString();
  42.  
    session.write(new Date());
  43.  
    System.out.println("接收到的数据:"+msg);
  44.  
     
  45.  
    }
  46.  
     
  47.  
    @Override
  48.  
    public void messageSent(IoSession session, Object message) throws Exception {
  49.  
    super.messageSent(session, message);
  50.  
    }
  51.  
    }
  52.  
    }

客户端

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

  • ConnectionManager.java
  1.  
    public class ConnectionManager {
  2.  
    private static final String BROADCAST_ACTION="com.commonlibrary.mina";
  3.  
    private static final String MESSAGE="message";
  4.  
    private ConnectionConfig mConfig;
  5.  
    private WeakReference<Context> mContext;
  6.  
    private NioSocketConnector mConnection;
  7.  
    private IoSession mSession;
  8.  
    private InetSocketAddress mAddress;
  9.  
     
  10.  
    public ConnectionManager(ConnectionConfig config) {
  11.  
    this.mConfig = config;
  12.  
    this.mContext = new WeakReference<Context>(config.getContext());
  13.  
    init();
  14.  
    }
  15.  
     
  16.  
    private void init() {
  17.  
    mAddress = new InetSocketAddress(mConfig.getIp(), mConfig.getPort());
  18.  
    mConnection = new NioSocketConnector();
  19.  
    mConnection.getSessionConfig().setReadBufferSize(mConfig.getReadBufferSize());
  20.  
    mConnection.getFilterChain().addLast("logger", new LoggingFilter());
  21.  
    mConnection.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
  22.  
    mConnection.setHandler(new DefaultHandler(mContext.get()));
  23.  
    mConnection.setDefaultRemoteAddress(mAddress);
  24.  
    }
  25.  
     
  26.  
    public boolean connect() {
  27.  
    try {
  28.  
    ConnectFuture future = mConnection.connect();
  29.  
    future.awaitUninterruptibly();
  30.  
    mSession = future.getSession();
  31.  
    SessionManager.getInstance().setSeesion(mSession);
  32.  
    } catch (Exception e) {
  33.  
    e.printStackTrace();
  34.  
    return false;
  35.  
    }
  36.  
    return mSession != null ? true:false;
  37.  
     
  38.  
    }
  39.  
    public void disConnection()
  40.  
    {
  41.  
    mConnection.dispose();
  42.  
    mConnection = null;
  43.  
    mSession = null;
  44.  
    mAddress = null;
  45.  
    mContext = null;
  46.  
    }
  47.  
     
  48.  
    private static class DefaultHandler extends IoHandlerAdapter {
  49.  
    private final Context mContext;
  50.  
     
  51.  
     
  52.  
    public DefaultHandler(Context context) {
  53.  
    this.mContext = context;
  54.  
    }
  55.  
     
  56.  
    @Override
  57.  
    public void sessionCreated(IoSession session) throws Exception {
  58.  
    super.sessionCreated(session);
  59.  
    }
  60.  
     
  61.  
    @Override
  62.  
    public void sessionOpened(IoSession session) throws Exception {
  63.  
    super.sessionOpened(session);
  64.  
    //将我们的session保存到我们的session manager类中, 从而可以发送消息到服务器
  65.  
    }
  66.  
     
  67.  
    @Override
  68.  
    public void sessionClosed(IoSession session) throws Exception {
  69.  
    super.sessionClosed(session);
  70.  
    }
  71.  
     
  72.  
    @Override
  73.  
    public void messageReceived(IoSession session, Object message) throws Exception {
  74.  
    super.messageReceived(session, message);
  75.  
    if (mContext != null)
  76.  
    {
  77.  
    Intent intent = new Intent(BROADCAST_ACTION);
  78.  
    intent.putExtra(MESSAGE, message.toString());
  79.  
    LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
  80.  
    }
  81.  
    }
  82.  
     
  83.  
    @Override
  84.  
    public void messageSent(IoSession session, Object message) throws Exception {
  85.  
    super.messageSent(session, message);
  86.  
     
  87.  
    }
  88.  
    }
  89.  
    }
  • ConnectionConfig.java
  1.  
    public class ConnectionConfig {
  2.  
    private Context context;
  3.  
    private String ip;
  4.  
    private int port;
  5.  
    private int readBufferSize;
  6.  
    private long connectionTimeout;
  7.  
     
  8.  
    public Context getContext() {
  9.  
    return context;
  10.  
    }
  11.  
     
  12.  
    public void setContext(Context context) {
  13.  
    this.context = context;
  14.  
    }
  15.  
     
  16.  
    public String getIp() {
  17.  
    return ip;
  18.  
    }
  19.  
     
  20.  
    public void setIp(String ip) {
  21.  
    this.ip = ip;
  22.  
    }
  23.  
     
  24.  
    public int getPort() {
  25.  
    return port;
  26.  
    }
  27.  
     
  28.  
    public void setPort(int port) {
  29.  
    this.port = port;
  30.  
    }
  31.  
     
  32.  
    public int getReadBufferSize() {
  33.  
    return readBufferSize;
  34.  
    }
  35.  
     
  36.  
    public void setReadBufferSize(int readBufferSize) {
  37.  
    this.readBufferSize = readBufferSize;
  38.  
    }
  39.  
     
  40.  
    public long getConnectionTimeout() {
  41.  
    return connectionTimeout;
  42.  
    }
  43.  
     
  44.  
    public void setConnectionTimeout(long connectionTimeout) {
  45.  
    this.connectionTimeout = connectionTimeout;
  46.  
    }
  47.  
     
  48.  
    //构建者模式
  49.  
    public static class Builder{
  50.  
    private Context context;
  51.  
    private String ip="10.90.24.139";
  52.  
    private int port=9123;
  53.  
    private int readBufferSize=10240;
  54.  
    private long connectionTimeout=10000;
  55.  
     
  56.  
    public Builder(Context context) {
  57.  
    this.context = context;
  58.  
    }
  59.  
     
  60.  
    public Builder setIp(String ip) {
  61.  
    this.ip = ip;
  62.  
    return this;
  63.  
    }
  64.  
     
  65.  
    public Builder setPort(int port) {
  66.  
    this.port = port;
  67.  
    return this;
  68.  
    }
  69.  
     
  70.  
    public Builder setReadBufferSize(int readBufferSize) {
  71.  
    this.readBufferSize = readBufferSize;
  72.  
    return this;
  73.  
    }
  74.  
     
  75.  
    public Builder setConnectionTimeout(long connectionTimeout) {
  76.  
    this.connectionTimeout = connectionTimeout;
  77.  
    return this;
  78.  
    }
  79.  
     
  80.  
    private void applyConfig(ConnectionConfig config)
  81.  
    {
  82.  
    config.context = this.context;
  83.  
    config.ip = this.ip;
  84.  
    config.port = this.port;
  85.  
    config.readBufferSize = readBufferSize;
  86.  
    config.connectionTimeout = this.connectionTimeout;
  87.  
    }
  88.  
    public ConnectionConfig builder()
  89.  
    {
  90.  
    ConnectionConfig config = new ConnectionConfig();
  91.  
    applyConfig(config);
  92.  
    return config;
  93.  
    }
  94.  
    }
  95.  
     
  96.  
    }
  • SessionManager.java
  1.  
    public class SessionManager {
  2.  
    private static SessionManager mInstance = null;
  3.  
    //最终与服务器进行通信的对象
  4.  
    private IoSession mSession;
  5.  
    public static SessionManager getInstance() {
  6.  
    if (mInstance == null)
  7.  
    {
  8.  
    synchronized (SessionManager.class) {
  9.  
    if (mInstance == null) {
  10.  
    mInstance = new SessionManager();
  11.  
    }
  12.  
    }
  13.  
    }
  14.  
    return mInstance;
  15.  
    }
  16.  
     
  17.  
    public void setSeesion(IoSession session){
  18.  
    this.mSession = session;
  19.  
    }
  20.  
    public SessionManager() {
  21.  
    }
  22.  
     
  23.  
    public SessionManager(IoSession mSession) {
  24.  
    this.mSession = mSession;
  25.  
    }
  26.  
     
  27.  
    /**
  28.  
    * 将对象写到服务端
  29.  
    * @param msg
  30.  
    */
  31.  
    public void writeToServer(Object msg)
  32.  
    {
  33.  
    if (mSession != null) {
  34.  
    mSession.write(msg);
  35.  
    }
  36.  
    }
  37.  
     
  38.  
    public void closeSession()
  39.  
    {
  40.  
    if (mSession != null)
  41.  
    mSession.closeOnFlush();
  42.  
    }
  43.  
    public void removeSession()
  44.  
    {
  45.  
    this.mSession = null;
  46.  
    }
  47.  
    }
  • MinaActivity.java
  1.  
    public class MinaActivity extends Activity implements View.OnClickListener{
  2.  
    private MessageBroadcastReceiver receiver = new MessageBroadcastReceiver();
  3.  
    private Button btn1, btn2;
  4.  
    private TextView message;
  5.  
    @Override
  6.  
    protected void onCreate(Bundle savedInstanceState) {
  7.  
    super.onCreate(savedInstanceState);
  8.  
    setContentView(R.layout.mina_test);
  9.  
    message = (TextView) findViewById(R.id.message);
  10.  
    btn1 = (Button) findViewById(R.id.btn1);
  11.  
    btn2 = (Button) findViewById(R.id.btn2);
  12.  
    btn1.setOnClickListener(this);
  13.  
    btn2.setOnClickListener(this);
  14.  
    registerBroadcast();
  15.  
    }
  16.  
     
  17.  
    private void registerBroadcast() {
  18.  
    IntentFilter filter = new IntentFilter("com.commonlibrary.mina");
  19.  
    LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter);
  20.  
    }
  21.  
     
  22.  
    private void unregisterBroadcast()
  23.  
    {
  24.  
    LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
  25.  
    }
  26.  
     
  27.  
    @Override
  28.  
    protected void onDestroy() {
  29.  
    super.onDestroy();
  30.  
    stopService(new Intent(this, MinaService.class));
  31.  
    unregisterBroadcast();
  32.  
    }
  33.  
     
  34.  
    @Override
  35.  
    public void onClick(View v) {
  36.  
    switch (v.getId())
  37.  
    {
  38.  
    case R.id.btn1:
  39.  
    SessionManager.getInstance().writeToServer("123");
  40.  
    break;
  41.  
    case R.id.btn2:
  42.  
    Intent intent = new Intent(this, MinaService.class);
  43.  
    startService(intent);
  44.  
    break;
  45.  
    }
  46.  
    }
  47.  
     
  48.  
     
  49.  
    private class MessageBroadcastReceiver extends BroadcastReceiver
  50.  
    {
  51.  
     
  52.  
    @Override
  53.  
    public void onReceive(Context context, Intent intent) {
  54.  
    message.setText(intent.getStringExtra("message"));
  55.  
    }
  56.  
    }
  57.  
    }

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

  • MinaService.java
  1.  
    public class MinaService extends Service {
  2.  
     
  3.  
    private ConnectionHandlerThread thread;
  4.  
    @Nullable
  5.  
    @Override
  6.  
    public IBinder onBind(Intent intent) {
  7.  
    return null;
  8.  
    }
  9.  
     
  10.  
    @Override
  11.  
    public void onCreate() {
  12.  
    super.onCreate();
  13.  
    thread = new ConnectionHandlerThread("mina", getApplicationContext());
  14.  
    System.out.println("service create:");
  15.  
    thread.start();
  16.  
    }
  17.  
     
  18.  
    @Override
  19.  
    public void onDestroy() {
  20.  
    super.onDestroy();
  21.  
    thread.disConnection();
  22.  
    }
  23.  
     
  24.  
    /**
  25.  
    * 负责调用ConnectionManager
  26.  
    */
  27.  
    class ConnectionHandlerThread extends HandlerThread {
  28.  
    private Context context;
  29.  
    boolean isConnection;
  30.  
    ConnectionManager mManager;
  31.  
     
  32.  
    public ConnectionHandlerThread(String name, Context context) {
  33.  
    super(name);
  34.  
    this.context = context;
  35.  
    ConnectionConfig config = new ConnectionConfig.Builder(context)
  36.  
    .setIp("10.90.24.139").setPort(9123)
  37.  
    .setReadBufferSize(10240).setReadBufferSize(10000).builder();
  38.  
    System.out.println(config.getReadBufferSize());
  39.  
    mManager = new ConnectionManager(config);
  40.  
    }
  41.  
     
  42.  
    @Override
  43.  
    protected void onLooperPrepared() {
  44.  
    super.onLooperPrepared();
  45.  
    while (true) {
  46.  
    isConnection = mManager.connect(); //
  47.  
     
  48.  
    if (isConnection) {
  49.  
    break;
  50.  
    }
  51.  
    try {
  52.  
    Thread.sleep(3000);
  53.  
    } catch (InterruptedException e) {
  54.  
    e.printStackTrace();
  55.  
    }
  56.  
    }
  57.  
     
  58.  
    }
  59.  
     
  60.  
    public void disConnection() {
  61.  
    mManager.disConnection();
  62.  
    }
  63.  
    }
  64.  
     
  65.  
    }

注意:
(1)局部广播的使用(LocalBroadcastManager)
(2)android中AlertDialog使用的构建者模式
(3)HandlerThread的使用

posted @ 2021-03-02 22:11  hanease  阅读(362)  评论(0编辑  收藏  举报