Android长连接之mina

一、概述

今天在工作一朋友Q我,问我长连接一般用什么做,我说我用的Mina,朋友表示没听过,于是打算写一篇相关的博文供大家讨论。

首先什么是mina?

它的官方定义:一个能够帮助用户开发高性能和高伸缩性网络应用程序的框架。它通过Java nio技术基于TCP/IP和UDP/IP协议提供了抽象的、事件驱动的、异步的API。

简单来说尼,就是一个优化过的长连接框架。

好了,先上图:


服务端的控制台显示

客户端发送“你好”,服务端控制台显示“服务端接收的消息:你好”,然后再返回字段“服务端给返回的消息:你好”客户端接收并显示。

好了,演示完毕,下面开始开启我们的代码之旅吧。


扫描二维码关注公众号,回复: 8939235 查看本文章

二、服务端搭建

/**
 * 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{

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

	private ConnectionManager mManager; // 连接管理
	private ConnectThread mConnectThread; // 线程
	
	@Override
	public void onCreate() {
		super.onCreate();
		
		mConnectThread = new ConnectThread("mina", getApplicationContext());
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		
		mConnectThread.start();
		return super.onStartCommand(intent, flags, startId);
	}
	
	@Override
	public void onDestroy() {
		// TODO Auto-generated method stub
		super.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);
		}
    	
		@Override
		protected void onLooperPrepared() {
			// TODO Auto-generated method stub
			super.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 block
				e.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); // 存储session
		return 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 {

	@Override
	public 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

发布了30 篇原创文章 · 获赞 78 · 访问量 15万+

猜你喜欢

转载自blog.csdn.net/u010302765/article/details/70821068