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{
-
-
-
public void sessionCreated(IoSession session) throws Exception {
-
super.sessionCreated(session);
-
}
-
-
-
public void sessionOpened(IoSession session) throws Exception {
-
super.sessionOpened(session);
-
}
-
-
-
public void sessionClosed(IoSession session) throws Exception {
-
super.sessionClosed(session);
-
}
-
-
-
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
-
super.exceptionCaught(session, cause);
-
}
-
-
-
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);
-
-
}
-
-
-
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;
-
}
-
-
-
public void sessionCreated(IoSession session) throws Exception {
-
super.sessionCreated(session);
-
}
-
-
-
public void sessionOpened(IoSession session) throws Exception {
-
super.sessionOpened(session);
-
//将我们的session保存到我们的session manager类中, 从而可以发送消息到服务器
-
}
-
-
-
public void sessionClosed(IoSession session) throws Exception {
-
super.sessionClosed(session);
-
}
-
-
-
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);
-
}
-
}
-
-
-
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;
-
-
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);
-
}
-
-
-
protected void onDestroy() {
-
super.onDestroy();
-
stopService(new Intent(this, MinaService.class));
-
unregisterBroadcast();
-
}
-
-
-
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
-
{
-
-
-
public void onReceive(Context context, Intent intent) {
-
message.setText(intent.getStringExtra("message"));
-
}
-
}
-
}
布局文件中就是两个按钮和一个文本控件,代码就不贴了。
- MinaService.java
-
public class MinaService extends Service {
-
-
private ConnectionHandlerThread thread;
-
-
-
public IBinder onBind(Intent intent) {
-
return null;
-
}
-
-
-
public void onCreate() {
-
super.onCreate();
-
thread = new ConnectionHandlerThread("mina", getApplicationContext());
-
System.out.println("service create:");
-
thread.start();
-
}
-
-
-
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);
-
}
-
-
-
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的使用