Android长连接之mina

2024-05-11 06:48
文章标签 android 连接 mina

本文主要是介绍Android长连接之mina,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、概述

今天在工作一朋友Q我,问我长连接一般用什么做,我说我用的Mina,朋友表示没听过,于是打算写一篇相关的博文供大家讨论。

首先什么是mina?

它的官方定义:一个能够帮助用户开发高性能和高伸缩性网络应用程序的框架。它通过Java nio技术基于TCP/IP和UDP/IP协议提供了抽象的、事件驱动的、异步的API。

简单来说尼,就是一个优化过的长连接框架。

好了,先上图:


服务端的控制台显示

客户端发送“你好”,服务端控制台显示“服务端接收的消息:你好”,然后再返回字段“服务端给返回的消息:你好”客户端接收并显示。

好了,演示完毕,下面开始开启我们的代码之旅吧。


二、服务端搭建

/*** Mina长连接服务端搭建* * @author 刘洋巴金* @date 2017-4-6* */
public class MinaService {public static void main(String[] args){// 继承IoService,服务器端接收器IoAcceptor acceptor = new NioSocketAcceptor();// 添加过滤器acceptor.getFilterChain().addLast("logf", new LoggingFilter()); // 日志过滤器acceptor.getFilterChain().addLast("objType", new ProtocolCodecFilter(new ObjectSerializationCodecFactory())); // 只能传输序列化后的对象// 回调acceptor.setHandler(new MyHandler());// 进行一些初始化配置acceptor.getSessionConfig().setReadBufferSize(2048); // 设置读缓存区大小acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); // 如果10秒钟没有任何读写,就设置成空闲状态。 BOTH_IDLE(读和写)// 设置监听端口号,开始监听了try {acceptor.bind(new InetSocketAddress(8888));System.out.println("启动成功");} catch (Exception e) {e.printStackTrace();}}
IoAcceptor:它主要负责在一个客户端和该服务之间创建连接。NioSocketAcceptor创建一个基于TCP/IP的非阻塞的server的socket。

acceptor.getFilterChain().addLast(...):添加一些过滤链。

handler:是个回调

然后进行一些初始化配置,比如超过几秒没有通讯就设置成空闲。

然后acceptor.bind(new InetSocketAddress(8888)); 设置监听的端口号,开始监听了。

/*** 负责session的创建及消息发送和接收的监听* */
private static class MyHandler extends IoHandlerAdapter{// session创建时回调public void sessionCreated(IoSession session) throws Exception {super.sessionCreated(session);System.out.println("sessionCreated " );}// session打开时回调public void sessionOpened(IoSession session) throws Exception {super.sessionOpened(session);System.out.println("sessionOpened " );}// 消息接收时回调public void messageReceived(IoSession session, Object message)throws Exception {super.messageReceived(session, message);String str = message.toString();session.write("服务端给返回的消息:"+str+"\n"); // 给客户端返回System.out.println("服务端接收消息: " + str);}// 消息发送时回调public void messageSent(IoSession session, Object message) throws Exception {super.messageSent(session, message);System.out.println("messageSent " );}// session关闭时回调public void sessionClosed(IoSession session) throws Exception {super.sessionClosed(session);}
}

handler用于回调各种状态。messageReceived 当客户端发送的消息到达时回调session.write(""); 给客户端返回


三、客户端搭建

/*** 参数配置* * @author 刘洋巴金* @date 2017-4-6* */
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;private int port;private int readBufferSize;private long connectionTimeout;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;}public ConnectionConfig builder(){ConnectionConfig config = new ConnectionConfig();config.context = this.context;config.ip = this.ip;config.port = this.port;config.readBufferSize = this.readBufferSize;config.connectionTimeout = this.connectionTimeout;return config;}
}
}
首先先创建个ConnectionConfig,该类用了构建者模式,该类主要作用是设置一些常用的属性。

/*** 连接管理器 * * @author 刘洋巴金* @date 2017-4-6* */
public class ConnectionManager {private ConnectionConfig config;private WeakReference<Context> mContext;private IoConnector mConnector;private SocketAddress mAddress;public ConnectionManager(ConnectionConfig config) {this.config = config;mContext = new WeakReference<Context>(config.getContext());// 初始化init();}/*** 初始化* */private void init() {// 继承IoService,客户端连接器mConnector = new NioSocketConnector();// 添加过滤器mConnector.getFilterChain().addLast("logf", new LoggingFilter()); // 日志过滤器mConnector.getFilterChain().addLast("objType", new ProtocolCodecFilter(new ObjectSerializationCodecFactory())); // 只能传输序列化后的对象// 回调mConnector.setHandler(new MyHandler(mContext.get()));// 进行一些初始化配置mConnector.getSessionConfig().setReadBufferSize(2048); // 设置读缓存区大小// 设置IP地址mAddress = new InetSocketAddress(config.getIp(), config.getPort());mConnector.setDefaultRemoteAddress(mAddress);}

创建连接管理者,基本配置与服务端搭建类似

NioSocketConnector创建客户端,然后设置过滤链,然后设置ip和端口号,config就是上面我们自己建的配置文件。然后设置handler进行回调。

/*** 负责session的创建及消息发送和接收的监听* */
private static class MyHandler extends IoHandlerAdapter{private Context mContext;private MyHandler(Context context){this.mContext = context;}// session打开时回调public void sessionOpened(IoSession session) throws Exception {super.sessionOpened(session);}// 消息接收时回调public void messageReceived(IoSession session, Object message)throws Exception {super.messageReceived(session, message);if(mContext != null){Intent intent = new Intent("com.bs.myMsg");intent.putExtra("message", message.toString());LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);}}// 消息发送时回调public void messageSent(IoSession session, Object message) throws Exception {super.messageSent(session, message);}
}

messageReceived接收服务端返回的消息,然后启动本地广播,本地广播的使用和正常广播使用一样,只是本地广播LocalBroadcastManager.getInstance(mContext)只能被本应用接收广播。其他应用接收不到该广播。

/*** 服务* * @author 刘洋巴金* @date 2017-4-6* */
public class MyService extends Service{@Overridepublic IBinder onBind(Intent intent) {// TODO Auto-generated method stubreturn null;}private ConnectionManager mManager; // 连接管理private ConnectThread mConnectThread; // 线程@Overridepublic void onCreate() {super.onCreate();mConnectThread = new ConnectThread("mina", getApplicationContext());}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {mConnectThread.start();return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.onDestroy();mConnectThread.disConnect();mConnectThread = null;}class ConnectThread extends HandlerThread{public ConnectThread(String name, Context context) {super(name);// 配置ConnectionConfig config = new ConnectionConfig.Builder(context).setIp("192.168.0.103").setPort(8888).setReadBufferSize(2048).setConnectionTimeout(10).builder();// 连接管理器mManager = new ConnectionManager(config);}@Overrideprotected void onLooperPrepared() {// TODO Auto-generated method stubsuper.onLooperPrepared();// 异步进行连接for (;;) {// 如果连接成功,则跳出循环if(mManager.connect()){Toast.makeText(getApplicationContext(), "连接成功", Toast.LENGTH_SHORT).show();break;}}// 3秒连接一次try {Thread.sleep(3000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}// 用于断开连接public void disConnect(){mManager.disConnect();}}
}

然后启动我们的服务,因为连接服务端是耗时操作,所以要放到子线程,我用的是HandlerThread,当然也可以自己写线程。

onLooperPrepared相当于run方法,我们做的操作是,在这里面一直进行连接,如果连接失败就继续连接,直至连接成功为止。

/*** 进行连接,并获取Session* */
public boolean connect() {try {ConnectFuture connectFuture = mConnector.connect(); // 可以查询连接操作的状态connectFuture.awaitUninterruptibly(); // 它会将程序阻塞住,直到连接创建好,所以,当这行代码结束后,// 就可以直接获取session。IoSession mSession = connectFuture.getSession();SessionManager.getInstance().setSession(mSession); // 存储sessionreturn true;} catch (Exception e) {e.printStackTrace();return false;}
}
ConnectionManager 中的connect方法,进行连接服务端,如果连接成功了,就返回true

这样就获取到了session,然我们存储起来,以便使用。

/*** 会话管理器 * * @author 刘洋巴金* @date 2017-4-6* */
public class SessionManager {private static SessionManager instance = new SessionManager();private SessionManager(){}public static SessionManager getInstance() {return instance;}private IoSession session;public IoSession getSession() {return session;}public void setSession(IoSession session) {this.session = session;}/*** 写消息* */public void writeMag(String msg){if(session != null){session.write(msg);}}/*** 关闭* */public void closeSession(){if(session != null){session.closeOnFlush();session = null;}}
}
然后是MainActivity的2个按钮的点击

@Override
public void onClick(View v) {switch (v.getId()) {case R.id.btn_conn: // 开启服务Intent in = new Intent(MainActivity.this, MyService.class);startService(in);break;case R.id.btn_sendMsg: // 发送消息// 文本消息String msg = et_msg.getText().toString();SessionManager.getInstance().writeMag(msg); // 发送消息break;default:break;}
}
先开启服务,连接服务端,然后是取出我们存储的session,给服务端发送消息。

然后再注册广播,接收ConnectionManager发送过来的广播。

/*** 注册消息广播* */
private void registerMsgReceiver() {mMsgReceiver = new MsgReceiver();IntentFilter filter = new IntentFilter("com.bs.myMsg");LocalBroadcastManager.getInstance(MainActivity.this).registerReceiver(mMsgReceiver, filter);
}/*** 广播接收* */
class MsgReceiver extends BroadcastReceiver {@Overridepublic void onReceive(Context context, Intent intent) {String msgStr = intent.getStringExtra("message");rmsg += msgStr+"\n";tv_msg_show.setText(rmsg);}
}
OK了。


四、mina流程


 1.远程服务器通过IoService与客户端建立连接,然后得到session。

 2.远程服务器发数据到我们的session

 3.然后我们的session会把我们的数据发送给IoFiterChain(过滤链)中进行过滤

 4.过滤链将符合条件的数据发送到Application layer(数据处理器)中,客户端做的就是根据IoHandler书写自己的业务逻辑。


五、demo

       Android长连接之mina

     

      ※1.首先记得改端口号和IP

          2.记得电脑和手机要在一个IP段上,也就是同一个wife

这篇关于Android长连接之mina的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/978804

相关文章

MySQL中的表连接原理分析

《MySQL中的表连接原理分析》:本文主要介绍MySQL中的表连接原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、背景2、环境3、表连接原理【1】驱动表和被驱动表【2】内连接【3】外连接【4编程】嵌套循环连接【5】join buffer4、总结1、背景

SpringBoot连接Redis集群教程

《SpringBoot连接Redis集群教程》:本文主要介绍SpringBoot连接Redis集群教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 依赖2. 修改配置文件3. 创建RedisClusterConfig4. 测试总结1. 依赖 <de

Android DataBinding 与 MVVM使用详解

《AndroidDataBinding与MVVM使用详解》本文介绍AndroidDataBinding库,其通过绑定UI组件与数据源实现自动更新,支持双向绑定和逻辑运算,减少模板代码,结合MV... 目录一、DataBinding 核心概念二、配置与基础使用1. 启用 DataBinding 2. 基础布局

Android ViewBinding使用流程

《AndroidViewBinding使用流程》AndroidViewBinding是Jetpack组件,替代findViewById,提供类型安全、空安全和编译时检查,代码简洁且性能优化,相比Da... 目录一、核心概念二、ViewBinding优点三、使用流程1. 启用 ViewBinding (模块级

java连接opcua的常见问题及解决方法

《java连接opcua的常见问题及解决方法》本文将使用EclipseMilo作为示例库,演示如何在Java中使用匿名、用户名密码以及证书加密三种方式连接到OPCUA服务器,若需要使用其他SDK,原理... 目录一、前言二、准备工作三、匿名方式连接3.1 匿名方式简介3.2 示例代码四、用户名密码方式连接4

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

MySQL 表的内外连接案例详解

《MySQL表的内外连接案例详解》本文给大家介绍MySQL表的内外连接,结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录表的内外连接(重点)内连接外连接表的内外连接(重点)内连接内连接实际上就是利用where子句对两种表形成的笛卡儿积进行筛选,我

Apache 高级配置实战之从连接保持到日志分析的完整指南

《Apache高级配置实战之从连接保持到日志分析的完整指南》本文带你从连接保持优化开始,一路走到访问控制和日志管理,最后用AWStats来分析网站数据,对Apache配置日志分析相关知识感兴趣的朋友... 目录Apache 高级配置实战:从连接保持到日志分析的完整指南前言 一、Apache 连接保持 - 性

电脑蓝牙连不上怎么办? 5 招教你轻松修复Mac蓝牙连接问题的技巧

《电脑蓝牙连不上怎么办?5招教你轻松修复Mac蓝牙连接问题的技巧》蓝牙连接问题是一些Mac用户经常遇到的常见问题之一,在本文章中,我们将提供一些有用的提示和技巧,帮助您解决可能出现的蓝牙连接问... 蓝牙作为一种流行的无线技术,已经成为我们连接各种设备的重要工具。在 MAC 上,你可以根据自己的需求,轻松地

宝塔安装的MySQL无法连接的情况及解决方案

《宝塔安装的MySQL无法连接的情况及解决方案》宝塔面板是一款流行的服务器管理工具,其中集成的MySQL数据库有时会出现连接问题,本文详细介绍两种最常见的MySQL连接错误:“1130-Hostisn... 目录一、错误 1130:Host ‘xxx.xxx.xxx.xxx’ is not allowed