Android-----借助MINA框架实现长连接、短连接以及断线重连

        前面我们分析了MINA框架的源码,大家可以从这里进行查看,这篇博客我们使用MINA来完成项目中经常会用到的长连接、短连接、断线重连功能,直接开始了;

        实现思路是:

        (1):为MINA客户端设置监听器,具体来讲的话就是实现MINA框架为我们提供的IoServiceListener接口,当客户端与服务端的Session会话关闭的时候,客户端会回调这个接口里面的sessionDestroyed方法,我们便可以在这个方法进行断线重连的操作了,我们设置断线重连的次数为5次,每个连接的超时时间是5s,也就是说如果1次连接在5s内没有得到响应的话,我们就认为这次连接失败了,进行重连,重连次数超过5次就认为服务器端出现了严重问题,或者是网络环境实在是太差了,结束断点重连;

        (2):除此之外,因为MINA底层实现是NIO,是有用到Buffer缓存的,因此我们使用MINA中的提供给我们的空闲检测机制进行了连接预关闭,客户端重连的操作,什么意思呢?就是说如果客户端在一段时间内没有向服务端发送数据的话,那么我们就会预关闭客户端服务端之间的Session会话,这样的话就可以清空该Session会话对应的Buffer缓存了,避免了如果下次操作需要更大缓存出现的缓存不足现象;具体实现是在客户端一段时间内没有向服务端传递数据的话,会回调服务端IoHandlerAdapter的sessionIdle方法,我们在这个方法里面调用IoSession的close方法,这样的话,客户端服务端之间的连接就断开了,相应的就会回调前面为客户端设置的sessionDestroyed方法,在这个方法里面进行重连就可以啦!

        (3):实际中,客户端在第一次连接服务器的时候一般是没有连接次数限制的,也就是一直尝试连接,直到你连接成功,当然你也可以设置连接次数限制;

        下面我附上实现主要代码,具体代码可以在文章末尾点击连接下载:

        服务端:

public class HeartBeatServer {
	public static void main(String[] args) {
		HeartBeatServer server = new HeartBeatServer();
		server.initServerMina(10000);
	}
	
	/**
	 * 初始化服务端MINA
	 * @param port 绑定的端口号
	 */
	public void initServerMina(int port)
	{
		NioSocketAcceptor acceptor = new NioSocketAcceptor();
		HeartBeatHandler handler = new HeartBeatHandler();//创建一个Handler对象用于处理业务逻辑
		acceptor.setHandler(handler);
		acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory()));//添加Filter对象
		acceptor.getSessionConfig().setReadBufferSize(2048);//设置读取数据缓存区的大小
		acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, AcceptorUtils.IDEL_TIME);//设置IDEL_TIME时间没有发送消息的话则处于空闲状态
		try {
			acceptor.bind(new InetSocketAddress(port));//绑定IP地址与端口号
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
        客户端:

public class MainActivity extends Activity {

	public Button mButton;
	public Intent intent = null;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mButton = (Button) findViewById(R.id.button);
		mButton.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View arg0) {
				//开启连接Service
				intent = new Intent(MainActivity.this, HeartBeatService.class);
				startService(intent);
			}
		});
	}
	
	@Override
	protected void onDestroy() {
		stopService(intent);
		super.onDestroy();
	}
}

public class HeartBeatService extends Service{
	
	@Override
	public void onCreate() {
		//开启单独的线程,因为Service是位于主线程的,为了避免主线程被阻塞
		HeartBeatThread thread = new HeartBeatThread();
		thread.start();
		super.onCreate();
	}
	
	class HeartBeatThread extends  Thread
	{
		@Override
		public void run() {
			initClientMina(ConnectUtils.HOST, ConnectUtils.PORT);
		}
	}
	/**
	 * 初始化客户端MINA
	 * @param host
	 * @param port
	 */
	public void initClientMina(String host,int port)
	{
	    NioSocketConnector connector = null;
		try {
			connector = new NioSocketConnector();
			HeartBeatHandler handler = new HeartBeatHandler();//创建handler对象,用于业务逻辑处理
			connector.setHandler(handler);
			connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory()));//添加Filter对象
		} catch (Exception e2) {
			e2.printStackTrace();
			System.out.println(e2.toString());
		}
		connector.setConnectTimeout(ConnectUtils.TIMEOUT);//设置连接超时时间
		int count = 0;//记录连接次数
		while(true)
		{
			try {
				count++;
				//执行到这里表示客户端刚刚启动需要连接服务器,第一次连接服务器的话是没有尝试次数限制的,但是随后的断线重连就有次数限制了
				ConnectFuture future = connector.connect(new InetSocketAddress(ConnectUtils.HOST, ConnectUtils.PORT));
				future.awaitUninterruptibly();//一直阻塞,直到连接建立
				IoSession session = future.getSession();//获取Session对象
				if(session.isConnected())
				{
					//表示连接成功
					System.out.println(ConnectUtils.stringNowTime()+" : 客户端连接服务器成功.....");
					break;
				}
			} catch (RuntimeIoException  e) {
				System.out.println(ConnectUtils.stringNowTime()+" : 第"+count+"次客户端连接服务器失败,因为"+ConnectUtils.TIMEOUT+"s没有连接成功");
				try {
					Thread.sleep(2000);//如果本次连接服务器失败,则间隔2s后进行重连操作
					System.out.println(ConnectUtils.stringNowTime()+" : 开始第"+(count+1)+"次连接服务器");
				} catch (InterruptedException e1) {
					e1.printStackTrace();
				}
			}
		}
		//为MINA客户端添加监听器,当Session会话关闭的时候,进行自动重连
		connector.addListener(new HeartBeatListener(connector));
	}
	@Override
	public IBinder onBind(Intent arg0) {
		return null;
	}
}
public class HeartBeatListener implements IoServiceListener {
	
	public NioSocketConnector connector;
	public HeartBeatListener(NioSocketConnector connector)
	{
		this.connector = connector;
	}
	@Override
	public void serviceActivated(IoService arg0) throws Exception {
	}

	@Override
	public void serviceDeactivated(IoService arg0) throws Exception {
	}

	@Override
	public void serviceIdle(IoService arg0, IdleStatus arg1) throws Exception {
	}

	@Override
	public void sessionClosed(IoSession arg0) throws Exception {
		System.out.println("hahahaha");
	}

	@Override
	public void sessionCreated(IoSession arg0) throws Exception {
	}

	@Override
	public void sessionDestroyed(IoSession arg0){
		repeatConnect("");
	}
	
	/*
	 * 断线重连操作 
	 * @param content
	 */
	public void repeatConnect(String content)
	{
		// 执行到这里表示Session会话关闭了,需要进行重连,我们设置每隔3s重连一次,如果尝试重连5次都没成功的话,就认为服务器端出现问题,不再进行重连操作
		int count = 0;// 记录尝试重连的次数
		while (true) {
			try {
				count++;// 重连次数加1
				ConnectFuture future = connector.connect(new InetSocketAddress(
						ConnectUtils.HOST, ConnectUtils.PORT));
				future.awaitUninterruptibly();// 一直阻塞住等待连接成功
				IoSession session = future.getSession();// 获取Session对象
				if (session.isConnected()) {
					// 表示重连成功
					System.out.println(content + ConnectUtils.stringNowTime() + " : 断线重连" + count
							+ "次之后成功.....");
					count = 0;
					break;
				}
			} catch (Exception e) {
				if (count == ConnectUtils.REPEAT_TIME) {
					System.out.println(content + ConnectUtils.stringNowTime() + " : 断线重连"
							+ ConnectUtils.REPEAT_TIME + "次之后仍然未成功,结束重连.....");
					break;
				} else
				{
					System.out.println(content + ConnectUtils.stringNowTime() + " : 本次断线重连失败,3s后进行第" + (count + 1) + "次重连.....");
					try {
						Thread.sleep(3000);
						System.out.println(content + ConnectUtils.stringNowTime() + " : 开始第"+(count + 1)+"次重连.....");
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
				}
			}
		}
	}
}
        我们来运行下程序,查看到客户端输出结果截图:

  

        (1):红色部分表示我们还没在启动服务端时候,客户端会一直尝试建立连接,连接超时时间设置为5s,连接失败之后,暂停2s进行连接,因为是第一次连接嘛,所以我们没有对尝试连接次数做出限制;

        (2):绿色部分是我们连接成功后,但是客户端10s内没有向服务端发送数据,那么这时候服务端就会调用sessionIdle方法来预关闭连接了,客户端回调IoServiceListener的sessionDestroyed方法在它里面进行重连操作,因为这时候服务端没挂且网络环境比较好,每次重连一次就成功了;

        (3):蓝色部分是我们将服务器关闭又开启之后的输出,可以看到尝试建立4次之后连接建立成功;

        (4):紫色部分是我们关闭服务器之后没有重新开启的输出,可以发现客户端在尝试重连5次之后不再进行连接操作;

        注意几点:

        (1):我们上面是通过在Service开启线程的方式来在后台运行长连接的建立和断线重连操作的,注意不能直接在Service里面进行这些操作,因为Service是位于主线程的,可能会带来ANR异常;

        (2):不要忘记添加网络权限:<uses-permission android:name="android.permission.INTERNET" />

        (3):如果你的应用是在已启动的时候就要进行长连接的建立,比如接收服务端的推送之类的功能,那么建议你通过闹钟服务来开启你的Service,这样的话能够保证你的长连接尽可能的存在;

        还有一点就是MINA也是支持短连接的,也就是请求/响应模式,具体来讲我们可以在服务端回应客户端之后关闭Session就可以了;

        点击下载源代码

  


  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值