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

相关文章

Android NDK版本迭代与FFmpeg交叉编译完全指南

《AndroidNDK版本迭代与FFmpeg交叉编译完全指南》在Android开发中,使用NDK进行原生代码开发是一项常见需求,特别是当我们需要集成FFmpeg这样的多媒体处理库时,本文将深入分析A... 目录一、android NDK版本迭代分界线二、FFmpeg交叉编译关键注意事项三、完整编译脚本示例四

Android与iOS设备MAC地址生成原理及Java实现详解

《Android与iOS设备MAC地址生成原理及Java实现详解》在无线网络通信中,MAC(MediaAccessControl)地址是设备的唯一网络标识符,本文主要介绍了Android与iOS设备M... 目录引言1. MAC地址基础1.1 MAC地址的组成1.2 MAC地址的分类2. android与I

Android 实现一个隐私弹窗功能

《Android实现一个隐私弹窗功能》:本文主要介绍Android实现一个隐私弹窗功能,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 效果图如下:1. 设置同意、退出、点击用户协议、点击隐私协议的函数参数2. 《用户协议》、《隐私政策》设置成可点击的,且颜色要区分出来res/l

Android实现一键录屏功能(附源码)

《Android实现一键录屏功能(附源码)》在Android5.0及以上版本,系统提供了MediaProjectionAPI,允许应用在用户授权下录制屏幕内容并输出到视频文件,所以本文将基于此实现一个... 目录一、项目介绍二、相关技术与原理三、系统权限与用户授权四、项目架构与流程五、环境配置与依赖六、完整

Android 12解决push framework.jar无法开机的方法小结

《Android12解决pushframework.jar无法开机的方法小结》:本文主要介绍在Android12中解决pushframework.jar无法开机的方法,包括编译指令、框架层和s... 目录1. android 编译指令1.1 framework层的编译指令1.2 替换framework.ja

Android开发环境配置避坑指南

《Android开发环境配置避坑指南》本文主要介绍了Android开发环境配置过程中遇到的问题及解决方案,包括VPN注意事项、工具版本统一、Gerrit邮箱配置、Git拉取和提交代码、MergevsR... 目录网络环境:VPN 注意事项工具版本统一:android Studio & JDKGerrit的邮

Android实现定时任务的几种方式汇总(附源码)

《Android实现定时任务的几种方式汇总(附源码)》在Android应用中,定时任务(ScheduledTask)的需求几乎无处不在:从定时刷新数据、定时备份、定时推送通知,到夜间静默下载、循环执行... 目录一、项目介绍1. 背景与意义二、相关基础知识与系统约束三、方案一:Handler.postDel

MySQL 多表连接操作方法(INNER JOIN、LEFT JOIN、RIGHT JOIN、FULL OUTER JOIN)

《MySQL多表连接操作方法(INNERJOIN、LEFTJOIN、RIGHTJOIN、FULLOUTERJOIN)》多表连接是一种将两个或多个表中的数据组合在一起的SQL操作,通过连接,... 目录一、 什么是多表连接?二、 mysql 支持的连接类型三、 多表连接的语法四、实战示例 数据准备五、连接的性

MySQL中的分组和多表连接详解

《MySQL中的分组和多表连接详解》:本文主要介绍MySQL中的分组和多表连接的相关操作,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友一起看看吧... 目录mysql中的分组和多表连接一、MySQL的分组(group javascriptby )二、多表连接(表连接会产生大量的数据垃圾)MySQL中的

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE