• 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的使用

  • 相关阅读:
    LDAP
    开源实时日志分析ELK平台部署
    js上传并且预览图片
    python logging的应用
    使用QQ第三方登录 并在父页面跳转刷新
    ubuntu sudo不能用的解决办法
    ubuntu 16.10安装nginx
    crontab 切割日志
    nginx的日志切割
    python对excel文件的读写操作
  • 原文地址:https://www.cnblogs.com/hanease/p/14471674.html
Copyright © 2020-2023  润新知