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

相关文章

JavaWeb项目创建、部署、连接数据库保姆级教程(tomcat)

《JavaWeb项目创建、部署、连接数据库保姆级教程(tomcat)》:本文主要介绍如何在IntelliJIDEA2020.1中创建和部署一个JavaWeb项目,包括创建项目、配置Tomcat服务... 目录简介:一、创建项目二、tomcat部署1、将tomcat解压在一个自己找得到路径2、在idea中添加

Android使用java实现网络连通性检查详解

《Android使用java实现网络连通性检查详解》这篇文章主要为大家详细介绍了Android使用java实现网络连通性检查的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录NetCheck.Java(可直接拷贝)使用示例(Activity/Fragment 内)权限要求

2025最新版Android Studio安装及组件配置教程(SDK、JDK、Gradle)

《2025最新版AndroidStudio安装及组件配置教程(SDK、JDK、Gradle)》:本文主要介绍2025最新版AndroidStudio安装及组件配置(SDK、JDK、Gradle... 目录原生 android 简介Android Studio必备组件一、Android Studio安装二、A

通过DBeaver连接GaussDB数据库的实战案例

《通过DBeaver连接GaussDB数据库的实战案例》DBeaver是一个通用的数据库客户端,可以通过配置不同驱动连接各种不同的数据库,:本文主要介绍通过DBeaver连接GaussDB数据库的... 目录​一、前置条件​二、连接步骤​三、常见问题与解决方案​1. 驱动未找到​2. 连接超时​3. 权限不

Navicat连接Mysql8.0.11出现1251错误的解决方案

《Navicat连接Mysql8.0.11出现1251错误的解决方案》在重装电脑并安装最新版MySQL后,Navicat和Sqlyog连接MySQL时遇到的1251和2058错误,通过将MySQL用户... 目录Navicat连接mysql8.0.11出现1251错误原因分析解决问题方法有两种总结Navic

Python连接Spark的7种方法大全

《Python连接Spark的7种方法大全》ApacheSpark是一个强大的分布式计算框架,广泛用于大规模数据处理,通过PySpark,Python开发者能够无缝接入Spark生态系统,本文给大家介... 目录第一章:python与Spark集成概述PySpark 的核心优势基本集成配置步骤启动一个简单的

Android实现图片浏览功能的示例详解(附带源码)

《Android实现图片浏览功能的示例详解(附带源码)》在许多应用中,都需要展示图片并支持用户进行浏览,本文主要为大家介绍了如何通过Android实现图片浏览功能,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

在Android中使用WebView在线查看PDF文件的方法示例

《在Android中使用WebView在线查看PDF文件的方法示例》在Android应用开发中,有时我们需要在客户端展示PDF文件,以便用户可以阅读或交互,:本文主要介绍在Android中使用We... 目录简介:1. WebView组件介绍2. 在androidManifest.XML中添加Interne

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

Android协程高级用法大全

《Android协程高级用法大全》这篇文章给大家介绍Android协程高级用法大全,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友跟随小编一起学习吧... 目录1️⃣ 协程作用域(CoroutineScope)与生命周期绑定Activity/Fragment 中手