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:
- // 创建服务器监听
- IoAcceptor acceptor = new NioSocketAcceptor();
- // 设置buffer的长度
- acceptor.getSessionConfig().setReadBufferSize(2048);
- // 设置连接超时时间
- 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
- public class MinaService {
- public static void main(String[] args) {
- IoAcceptor acceptor = new NioSocketAcceptor();
- acceptor.getFilterChain().addLast( "logger", new LoggingFilter() );
- acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
- acceptor.setHandler(new DemoServiceHandler());
- acceptor.getSessionConfig().setMaxReadBufferSize(2048);
- acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); //10秒没有读写就进入空闲状态
- try {
- acceptor.bind(new InetSocketAddress(9123));
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- private static class DemoServiceHandler extends IoHandlerAdapter{
- @Override
- public void sessionCreated(IoSession session) throws Exception {
- super.sessionCreated(session);
- }
- @Override
- public void sessionOpened(IoSession session) throws Exception {
- super.sessionOpened(session);
- }
- @Override
- public void sessionClosed(IoSession session) throws Exception {
- super.sessionClosed(session);
- }
- @Override
- public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
- super.exceptionCaught(session, cause);
- }
- @Override
- public void messageReceived(IoSession session, Object message) throws Exception {
- super.messageReceived(session, message);
- String msg = message.toString();
- session.write(new Date());
- System.out.println("接收到的数据:"+msg);
- }
- @Override
- public void messageSent(IoSession session, Object message) throws Exception {
- super.messageSent(session, message);
- }
- }
- }
客户端
相关jar包
(1)mina-core-2.0.16.jar
(2)slf4j-android-1.6.1-RC1.jar
- ConnectionManager.java
- public class ConnectionManager {
- private static final String BROADCAST_ACTION="com.commonlibrary.mina";
- private static final String MESSAGE="message";
- private ConnectionConfig mConfig;
- private WeakReference<Context> mContext;
- private NioSocketConnector mConnection;
- private IoSession mSession;
- private InetSocketAddress mAddress;
- public ConnectionManager(ConnectionConfig config) {
- this.mConfig = config;
- this.mContext = new WeakReference<Context>(config.getContext());
- init();
- }
- private void init() {
- mAddress = new InetSocketAddress(mConfig.getIp(), mConfig.getPort());
- mConnection = new NioSocketConnector();
- mConnection.getSessionConfig().setReadBufferSize(mConfig.getReadBufferSize());
- mConnection.getFilterChain().addLast("logger", new LoggingFilter());
- mConnection.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
- mConnection.setHandler(new DefaultHandler(mContext.get()));
- mConnection.setDefaultRemoteAddress(mAddress);
- }
- public boolean connect() {
- try {
- ConnectFuture future = mConnection.connect();
- future.awaitUninterruptibly();
- mSession = future.getSession();
- SessionManager.getInstance().setSeesion(mSession);
- } catch (Exception e) {
- e.printStackTrace();
- return false;
- }
- return mSession != null ? true:false;
- }
- public void disConnection()
- {
- mConnection.dispose();
- mConnection = null;
- mSession = null;
- mAddress = null;
- mContext = null;
- }
- private static class DefaultHandler extends IoHandlerAdapter {
- private final Context mContext;
- public DefaultHandler(Context context) {
- this.mContext = context;
- }
- @Override
- public void sessionCreated(IoSession session) throws Exception {
- super.sessionCreated(session);
- }
- @Override
- public void sessionOpened(IoSession session) throws Exception {
- super.sessionOpened(session);
- //将我们的session保存到我们的session manager类中, 从而可以发送消息到服务器
- }
- @Override
- public void sessionClosed(IoSession session) throws Exception {
- super.sessionClosed(session);
- }
- @Override
- public void messageReceived(IoSession session, Object message) throws Exception {
- super.messageReceived(session, message);
- if (mContext != null)
- {
- Intent intent = new Intent(BROADCAST_ACTION);
- intent.putExtra(MESSAGE, message.toString());
- LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
- }
- }
- @Override
- public void messageSent(IoSession session, Object message) throws Exception {
- super.messageSent(session, message);
- }
- }
- }
- ConnectionConfig.java
- public class ConnectionConfig {
- private Context context;
- private String ip;
- private int port;
- private int readBufferSize;
- private long connectionTimeout;
- public Context getContext() {
- return context;
- }
- public void setContext(Context context) {
- this.context = context;
- }
- public String getIp() {
- return ip;
- }
- public void setIp(String ip) {
- this.ip = ip;
- }
- public int getPort() {
- return port;
- }
- public void setPort(int port) {
- this.port = port;
- }
- public int getReadBufferSize() {
- return readBufferSize;
- }
- public void setReadBufferSize(int readBufferSize) {
- this.readBufferSize = readBufferSize;
- }
- public long getConnectionTimeout() {
- return connectionTimeout;
- }
- public void setConnectionTimeout(long connectionTimeout) {
- this.connectionTimeout = connectionTimeout;
- }
- //构建者模式
- public static class Builder{
- private Context context;
- private String ip="10.90.24.139";
- private int port=9123;
- private int readBufferSize=10240;
- private long connectionTimeout=10000;
- 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;
- }
- private void applyConfig(ConnectionConfig config)
- {
- config.context = this.context;
- config.ip = this.ip;
- config.port = this.port;
- config.readBufferSize = readBufferSize;
- config.connectionTimeout = this.connectionTimeout;
- }
- public ConnectionConfig builder()
- {
- ConnectionConfig config = new ConnectionConfig();
- applyConfig(config);
- return config;
- }
- }
- }
- SessionManager.java
- public class SessionManager {
- private static SessionManager mInstance = null;
- //最终与服务器进行通信的对象
- private IoSession mSession;
- public static SessionManager getInstance() {
- if (mInstance == null)
- {
- synchronized (SessionManager.class) {
- if (mInstance == null) {
- mInstance = new SessionManager();
- }
- }
- }
- return mInstance;
- }
- public void setSeesion(IoSession session){
- this.mSession = session;
- }
- public SessionManager() {
- }
- public SessionManager(IoSession mSession) {
- this.mSession = mSession;
- }
- /**
- * 将对象写到服务端
- * @param msg
- */
- public void writeToServer(Object msg)
- {
- if (mSession != null) {
- mSession.write(msg);
- }
- }
- public void closeSession()
- {
- if (mSession != null)
- mSession.closeOnFlush();
- }
- public void removeSession()
- {
- this.mSession = null;
- }
- }
- MinaActivity.java
- public class MinaActivity extends Activity implements View.OnClickListener{
- private MessageBroadcastReceiver receiver = new MessageBroadcastReceiver();
- private Button btn1, btn2;
- private TextView message;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.mina_test);
- message = (TextView) findViewById(R.id.message);
- btn1 = (Button) findViewById(R.id.btn1);
- btn2 = (Button) findViewById(R.id.btn2);
- btn1.setOnClickListener(this);
- btn2.setOnClickListener(this);
- registerBroadcast();
- }
- private void registerBroadcast() {
- IntentFilter filter = new IntentFilter("com.commonlibrary.mina");
- LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter);
- }
- private void unregisterBroadcast()
- {
- LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver);
- }
- @Override
- protected void onDestroy() {
- super.onDestroy();
- stopService(new Intent(this, MinaService.class));
- unregisterBroadcast();
- }
- @Override
- public void onClick(View v) {
- switch (v.getId())
- {
- case R.id.btn1:
- SessionManager.getInstance().writeToServer("123");
- break;
- case R.id.btn2:
- Intent intent = new Intent(this, MinaService.class);
- startService(intent);
- break;
- }
- }
- private class MessageBroadcastReceiver extends BroadcastReceiver
- {
- @Override
- public void onReceive(Context context, Intent intent) {
- message.setText(intent.getStringExtra("message"));
- }
- }
- }
布局文件中就是两个按钮和一个文本控件,代码就不贴了。
- MinaService.java
- public class MinaService extends Service {
- private ConnectionHandlerThread thread;
- @Nullable
- @Override
- public IBinder onBind(Intent intent) {
- return null;
- }
- @Override
- public void onCreate() {
- super.onCreate();
- thread = new ConnectionHandlerThread("mina", getApplicationContext());
- System.out.println("service create:");
- thread.start();
- }
- @Override
- public void onDestroy() {
- super.onDestroy();
- thread.disConnection();
- }
- /**
- * 负责调用ConnectionManager
- */
- class ConnectionHandlerThread extends HandlerThread {
- private Context context;
- boolean isConnection;
- ConnectionManager mManager;
- public ConnectionHandlerThread(String name, Context context) {
- super(name);
- this.context = context;
- ConnectionConfig config = new ConnectionConfig.Builder(context)
- .setIp("10.90.24.139").setPort(9123)
- .setReadBufferSize(10240).setReadBufferSize(10000).builder();
- System.out.println(config.getReadBufferSize());
- mManager = new ConnectionManager(config);
- }
- @Override
- protected void onLooperPrepared() {
- super.onLooperPrepared();
- while (true) {
- isConnection = mManager.connect(); //
- if (isConnection) {
- break;
- }
- try {
- Thread.sleep(3000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- public void disConnection() {
- mManager.disConnection();
- }
- }
- }
注意:
(1)局部广播的使用(LocalBroadcastManager)
(2)android中AlertDialog使用的构建者模式
(3)HandlerThread的使用
mina保持android端\服务端的长连接-copy的更多相关文章
- Android从服务端获取json解析显示在客户端上面
Android从服务端获取json解析显示在客户端上面 百度经验:jingyan.baidu.com 首先说一下Json数据的最基本的特点,Json数据是一系列的键值对的集合,和XML数据来比,Jso ...
- Mina学习+手写服务端+通过telnet连接服务端
1. 2. 3. 4.MinaServer.java package com.mina; import java.io.IOException;import java.net.InetSocketAd ...
- 通过netty实现服务端与客户端的长连接通讯,及心跳检测。
基本思路:netty服务端通过一个Map保存所有连接上来的客户端SocketChannel,客户端的Id作为Map的key.每次服务器端如果要向某个客户端发送消息,只需根据ClientId取出对应的S ...
- android与服务端通讯时使用到的GZIP压缩及解压
为了减小android项目与服务端进行通讯时的数据流量,我们可以使用GZIP对服务端传输的数据进行压缩,在android客户端解压.或在客户端压缩,在服务端解压.代码如下: android客户端的GZ ...
- 保持WCF服务端与客户端的长连接
背景 客户端与服务端使用WCF建立连接后:1.可能长时间不对话(调用服务操作):2.客户端的网络不稳定. 为服务端与客户端两边都写“心跳检测”代码?不愿意. 解决 设置inactivityTimeou ...
- Android 解决服务端验证码问题
服务端验证码解决方法. 在服务端生成验证码后会把验证码字符串存在服务端的session中,等待用户提交进行比对.为了保证服务器与客户端的一对一的关系,所以出现了session 和cookie技术.客户 ...
- Swift3.0服务端开发(五) 记事本的开发(iOS端+服务端)
前边以及陆陆续续的介绍了使用Swift3.0开发的服务端应用程序的Perfect框架.本篇博客就做一个阶段性的总结,做一个完整的实例,其实这个实例在<Swift3.0服务端开发(一)>这篇 ...
- python socket 客服端服务端编程
客服端编程 import socket try: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) except socket.error a ...
- 转 互联网推送服务原理:长连接+心跳机制(MQTT协议)
http://blog.csdn.net/zhangzeyuaaa/article/details/39028369 目录(?)[-] 无线移动网络的特点 android系统的推送和IOS的推送有什么 ...
- 互联网推送服务原理:长连接+心跳机制(MQTT协议)
互联网推送消息的方式很常见,特别是移动互联网上,手机每天都能收到好多推送消息,经过研究发现,这些推送服务的原理都是维护一个长连接(要不不可能达到实时效果),但普通的socket连接对服务器的消耗太大了 ...
随机推荐
- LLM应用实战: AI资讯的自动聚合及报告生成
1.背景 花了整整两天时间,本qiang~开发了一个关于AI新闻资讯的自动聚合及报告生成工具. 本篇记录一下整体的框架和实现原理,并且本着它山之石可以攻玉,本qiang~开放了所有的源码,源码可见如下 ...
- 新一代AI换脸更自然,DeepLiveCam下载介绍(可直播)
DeepLiveCam是一款基于人工智能的图片替换工具,专注于提供实时人脸交换和一键视频深度伪造(deepfake)技术,能通过使用单张图片,在视频或直播中实现高精度的人脸替换 DeepLiveCam ...
- 基于Java+SpringBoot+Mysql实现的古诗词平台功能设计与实现二
一.前言介绍: 1.1 项目摘要 随着信息技术的迅猛发展和数字化时代的到来,传统文化与现代科技的融合已成为一种趋势.古诗词作为中华民族的文化瑰宝,具有深厚的历史底蕴和独特的艺术魅力.然而,在现代社会中 ...
- getRawType:获取数据类型,返回结果为 Number、String、Object、Array等
function getRawType(value) { return Object.prototype.toString.call(value).slice(8, -1)}//getoRawType ...
- JS中如何获取当前日期,并与输入日期作比较
首先我们获取到"2020-5-5"类型的值 通过Date函数转换 var inputDate = new Date(Date.parse(realTimeEnd)); 获取当前时间 ...
- IT人写好简历的原则与方法
来源: 51cto 发布时间: 2010-03-19 14:49 阅读: 3270 次 推荐: 3 原文链接 [收藏] 时常,在各大论坛看到不少的朋友在张贴简历,希望得到他人的指点. ...
- golang不同版本特性
Go 1.0[1] - 2012 年 3 月: 随着 Go 第一个版本发布的还有一份兼容性说明文档[2] .该文档承诺,Go 的未来版本会尽可能确保向后兼容性,不会破坏现有程序. For insta ...
- Codeforces Round 856 (Div2)
Counting Factorizations 任何一个正整数 \(m\) 都可以被唯一的分解为 \(p_1^{e_1} \cdot p_2^{e_2} \ldots p_k^{e_k}\) 的形式. ...
- 升级Linux内核版本
```shell# 查看内核版本,jw版本ceph默认format=2, 2.x 及之前的的内核版本需手动调整format=1# 4.x之前要关闭object-map fast-diff deep-f ...
- GIT 使用SSH 方式提交代码
1.需求 一般情况下,我们在提交代码的时候,使用 HTTP的方式提交代码,这种方式有一个问题,提交时需要输入账号和密码,这个就不是很安全,git 提供了 SSH的方式. 下面就实际操作一下如何使用ss ...