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

相关文章

Go语言连接MySQL数据库执行基本的增删改查

《Go语言连接MySQL数据库执行基本的增删改查》在后端开发中,MySQL是最常用的关系型数据库之一,本文主要为大家详细介绍了如何使用Go连接MySQL数据库并执行基本的增删改查吧... 目录Go语言连接mysql数据库准备工作安装 MySQL 驱动代码实现运行结果注意事项Go语言执行基本的增删改查准备工作

python连接sqlite3简单用法完整例子

《python连接sqlite3简单用法完整例子》SQLite3是一个内置的Python模块,可以通过Python的标准库轻松地使用,无需进行额外安装和配置,:本文主要介绍python连接sqli... 目录1. 连接到数据库2. 创建游标对象3. 创建表4. 插入数据5. 查询数据6. 更新数据7. 删除

Android 缓存日志Logcat导出与分析最佳实践

《Android缓存日志Logcat导出与分析最佳实践》本文全面介绍AndroidLogcat缓存日志的导出与分析方法,涵盖按进程、缓冲区类型及日志级别过滤,自动化工具使用,常见问题解决方案和最佳实... 目录android 缓存日志(Logcat)导出与分析全攻略为什么要导出缓存日志?按需过滤导出1. 按

在 Spring Boot 中连接 MySQL 数据库的详细步骤

《在SpringBoot中连接MySQL数据库的详细步骤》本文介绍了SpringBoot连接MySQL数据库的流程,添加依赖、配置连接信息、创建实体类与仓库接口,通过自动配置实现数据库操作,... 目录一、添加依赖二、配置数据库连接三、创建实体类四、创建仓库接口五、创建服务类六、创建控制器七、运行应用程序八

解决hive启动时java.net.ConnectException:拒绝连接的问题

《解决hive启动时java.net.ConnectException:拒绝连接的问题》Hadoop集群连接被拒,需检查集群是否启动、关闭防火墙/SELinux、确认安全模式退出,若问题仍存,查看日志... 目录错误发生原因解决方式1.关闭防火墙2.关闭selinux3.启动集群4.检查集群是否正常启动5.

在Linux系统上连接GitHub的方法步骤(适用2025年)

《在Linux系统上连接GitHub的方法步骤(适用2025年)》在2025年,使用Linux系统连接GitHub的推荐方式是通过SSH(SecureShell)协议进行身份验证,这种方式不仅安全,还... 目录步骤一:检查并安装 Git步骤二:生成 SSH 密钥步骤三:将 SSH 公钥添加到 github

Redis客户端连接机制的实现方案

《Redis客户端连接机制的实现方案》本文主要介绍了Redis客户端连接机制的实现方案,包括事件驱动模型、非阻塞I/O处理、连接池应用及配置优化,具有一定的参考价值,感兴趣的可以了解一下... 目录1. Redis连接模型概述2. 连接建立过程详解2.1 连php接初始化流程2.2 关键配置参数3. 最大连

Android Paging 分页加载库使用实践

《AndroidPaging分页加载库使用实践》AndroidPaging库是Jetpack组件的一部分,它提供了一套完整的解决方案来处理大型数据集的分页加载,本文将深入探讨Paging库... 目录前言一、Paging 库概述二、Paging 3 核心组件1. PagingSource2. Pager3.

C#连接SQL server数据库命令的基本步骤

《C#连接SQLserver数据库命令的基本步骤》文章讲解了连接SQLServer数据库的步骤,包括引入命名空间、构建连接字符串、使用SqlConnection和SqlCommand执行SQL操作,... 目录建议配合使用:如何下载和安装SQL server数据库-CSDN博客1. 引入必要的命名空间2.

Android kotlin中 Channel 和 Flow 的区别和选择使用场景分析

《Androidkotlin中Channel和Flow的区别和选择使用场景分析》Kotlin协程中,Flow是冷数据流,按需触发,适合响应式数据处理;Channel是热数据流,持续发送,支持... 目录一、基本概念界定FlowChannel二、核心特性对比数据生产触发条件生产与消费的关系背压处理机制生命周期