Mina 断线重连
定义:这里讨论的Mina 断线重连是指使用mina作为客户端软件,连接其他提供Socket通讯服务的服务器端。Socket服务器可以是Mina提供的服务器,也可以是C++提供的服务器。
一、断线重连的方式;
1. 在创建Mina客户端时增加一个监听器,或者增加一个拦截器,当检测到Session关闭时,自动进行重连。
2. 在第1种方式的基础上,增加客户端的读写通道空闲检查,当发生Session关闭或者读写空闲时,进行重连。
第一种方式比较传统,优点是简单方便,适合网络稳定、数据量不大(1M带宽以下)的环境;不过缺点是不能对系统级的连接断开阻塞进行捕获。
第二种方式更加精细,基本上能捕获到应用、网络、系统级的断连。
二、重连目的:
在使用Mina做为客户端时,往往因为网络、服务器、应用程序出现问题而导致连接断开,而自动重连,就是解决连接断开的唯一方式。如果网线断开、服务器宕机、应用程序挂了,都是断线的原因,这个时候,通过增加一个监听器或者拦截器,就能实现重连。但是生产环境中,断线的原因可能更复杂:网络不稳定、延时、服务器负载高、服务器或者应用程序的发送或者接收缓冲区满等等问题都可能导致数据传输过程出现类似于断线的情况,这个时候,光检测Session关闭是远远不够的,这个时候就需要一种重连机制,比如读写空闲超过30秒,就进行重连。对于数据不间断、实时性高、数据量大的应用场景,更是实用。
三、实例:
第一种:监听器方式
创建一个监听器实现mina的IoServiceListener接口,里面的方法可以不用写实现
import org.apache.mina.core.service.IoService;
import org.apache.mina.core.service.IoServiceListener;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
public class IoListener implements IoServiceListener{
@Override
public void serviceActivated(IoService arg0) throws Exception {
// TODO Auto-generated method stub
}
@Override
public void serviceDeactivated(IoService arg0) throws Exception {
// TODO Auto-generated method stub
}
@Override
public void serviceIdle(IoService arg0, IdleStatus arg1) throws Exception {
// TODO Auto-generated method stub
}
@Override
public void sessionCreated(IoSession arg0) throws Exception {
// TODO Auto-generated method stub
}
@Override
public void sessionDestroyed(IoSession arg0) throws Exception {
// TODO Auto-generated method stub
}
}
再创建客户端时加入监听
NioSocketConnector connector = new NioSocketConnector(); //创建连接客户端
connector.setConnectTimeoutMillis(30000); //设置连接超时
connector.getSessionConfig().setReceiveBufferSize(10240); // 设置接收缓冲区的大小
connector.getSessionConfig().setSendBufferSize(10240);// 设置输出缓冲区的大小
// 加入解码器
TextLineCodecFactory factory = new TextLineCodecFactory(Charset.forName("GBK"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue());
factory.setDecoderMaxLineLength(10240);
factory.setEncoderMaxLineLength(10240);
connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(factory));
connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));// 设置默认访问地址
//添加处理器
connector.setHandler(new IoHandler());
// 添加重连监听
connector.addListener(new IoListener() {
@Override
public void sessionDestroyed(IoSession arg0) throws Exception {
for (;;) {
try {
Thread.sleep(3000);
ConnectFuture future = connector.connect();
future.awaitUninterruptibly();// 等待连接创建成功
session = future.getSession();// 获取会话
if (session.isConnected()) {
logger.info("断线重连[" + connector.getDefaultRemoteAddress().getHostName() + ":" + connector.getDefaultRemoteAddress().getPort() + "]成功");
break;
}
} catch (Exception ex) {
logger.info("重连服务器登录失败,3秒再连接一次:" + ex.getMessage());
}
}
}
});
for (;;) {
try {
ConnectFuture future = connector.connect();
future.awaitUninterruptibly(); // 等待连接创建成功
session = future.getSession(); // 获取会话
logger.info("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
break;
} catch (RuntimeIoException e) {
logger.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e);
Thread.sleep(5000);// 连接失败后,重连间隔5s
}
}
第一种:拦截器方式
connector = new NioSocketConnector(); //创建连接客户端
connector.setConnectTimeoutMillis(30000); //设置连接超时
// 断线重连回调拦截器
connector.getFilterChain().addFirst("reconnection", new IoFilterAdapter() {
@Override
public void sessionClosed(NextFilter nextFilter, IoSession ioSession) throws Exception {
for(;;){
try{
Thread.sleep(3000);
ConnectFuture future = connector.connect();
future.awaitUninterruptibly();// 等待连接创建成功
session = future.getSession();// 获取会话
if(session.isConnected()){
logger.info("断线重连["+ connector.getDefaultRemoteAddress().getHostName() +":"+ connector.getDefaultRemoteAddress().getPort()+"]成功");
break;
}
}catch(Exception ex){
logger.info("重连服务器登录失败,3秒再连接一次:" + ex.getMessage());
}
}
}
});
TextLineCodecFactory factory = new TextLineCodecFactory(Charset.forName(encoding), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue());
factory.setDecoderMaxLineLength(10240);
factory.setEncoderMaxLineLength(10240);
//加入解码器
connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(factory));
//添加处理器
connector.setHandler(new IoHandler());
connector.getSessionConfig().setReceiveBufferSize(10240); // 设置接收缓冲区的大小
connector.getSessionConfig().setSendBufferSize(10240); // 设置输出缓冲区的大小
connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));// 设置默认访问地址
for (;;) {
try {
ConnectFuture future = connector.connect();
// 等待连接创建成功
future.awaitUninterruptibly();
// 获取会话
session = future.getSession();
logger.error("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
break;
} catch (RuntimeIoException e) {
logger.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e);
Thread.sleep(5000);// 连接失败后,重连间隔5s
}
}
第二种:加入空闲检测机制
空闲检测机制需要在创建客户端时,加入空闲超时,然后在处理器handler端的sessionIdle方法中加入一个预关闭连接的方法。让Session关闭传递到监听器或者拦截器的sessionClose方法中实现重连。
以拦截器方式为例,在创建客户端时,加入读写通道空闲检查超时机制。
connector = new NioSocketConnector(); //创建连接客户端
connector.setConnectTimeoutMillis(30000); //设置连接超时
// 断线重连回调拦截器
connector.getFilterChain().addFirst("reconnection", new IoFilterAdapter() {
@Override
public void sessionClosed(NextFilter nextFilter, IoSession ioSession) throws Exception {
for(;;){
try{
Thread.sleep(3000);
ConnectFuture future = connector.connect();
future.awaitUninterruptibly();// 等待连接创建成功
session = future.getSession();// 获取会话
if(session.isConnected()){
logger.info("断线重连["+ connector.getDefaultRemoteAddress().getHostName() +":"+ connector.getDefaultRemoteAddress().getPort()+"]成功");
break;
}
}catch(Exception ex){
logger.info("重连服务器登录失败,3秒再连接一次:" + ex.getMessage());
}
}
}
});
connector.getFilterChain().addLast("mdc", new MdcInjectionFilter());
TextLineCodecFactory factory = new TextLineCodecFactory(Charset.forName(encoding), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue());
factory.setDecoderMaxLineLength(10240);
factory.setEncoderMaxLineLength(10240);
//加入解码器
connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(factory));
connector.getSessionConfig().setReceiveBufferSize(10240); // 设置接收缓冲区的大小
connector.getSessionConfig().setSendBufferSize(10240);// 设置输出缓冲区的大小
connector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30000); //读写都空闲时间:30秒
connector.getSessionConfig().setIdleTime(IdleStatus.READER_IDLE, 40000);//读(接收通道)空闲时间:40秒
connector.getSessionConfig().setIdleTime(IdleStatus.WRITER_IDLE, 50000);//写(发送通道)空闲时间:50秒
//添加处理器
connector.setHandler(new IoHandler());
connector.setDefaultRemoteAddress(new InetSocketAddress(host, port));// 设置默认访问地址
for (;;) {
try {
ConnectFuture future = connector.connect();
// 等待连接创建成功
future.awaitUninterruptibly();
// 获取会话
session = future.getSession();
logger.error("连接服务端" + host + ":" + port + "[成功]" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
break;
} catch (RuntimeIoException e) {
System.out.println("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage());
logger.error("连接服务端" + host + ":" + port + "失败" + ",,时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + ", 连接MSG异常,请检查MSG端口、IP是否正确,MSG服务是否启动,异常内容:" + e.getMessage(), e);
Thread.sleep(5000);// 连接失败后,重连10次,间隔30s
}
}
然后在数据处理器IoHandler中sessionIdle方法中加入Session会话关闭的代码,这样session关闭就能传递到拦截器或者监听器中,然后实现重连。
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
public class IoHandler extends IoHandlerAdapter {
//部分代码忽略...
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
logger.info("-客户端与服务端连接[空闲] - " + status.toString());
if(session != null){
session.close(true);
}
}
//部分代码忽略...
}
总结-最佳实践:
以上两种方式我个人认为最好是使用第二种。在实际的生产环境,对于数据量比较少的情况下,需要加一个线程专门发送心跳信息,然后在服务器端进行回应心跳,这样就保证读写通道不出现空闲。如果数据量比较大,大到24小时都有数据,那么就不需要心跳线程,可以直接在IoHandler处理器端中messageReceived方法中定时发送心跳到服务器。由于读写监控还可以处理服务器、网络、应用等等方面的不确定因素,所以建议使用第二种方式。
另外有几点注意事项:
第一:断线重连是针对长连接的,也就是说,连接后两端一直在发送数据。
第二:断线重连是针对客户端的,如果你在服务端使用,可能会有根据场景导致失败。因为服务端会自动发送心跳。
第二:断线重连如果测试设置接收超时情况,就应该一直发送数据,而服务端只接收不发送,心跳数据也不发送,到时间后才会起作用。
相关推荐
当我们遇到"MINA断线重连死锁解决"的问题时,通常涉及到的是网络连接的稳定性和并发处理的复杂性。 MINA框架提供了非阻塞I/O模型,允许处理大量并发连接。然而,在某些情况下,特别是在断线重连的机制中,可能会...
然后,我们来看"Mina断线重连"。当检测到连接丢失时,Mina提供了断线重连功能,以确保服务的连续性和可靠性。实现这一功能通常包括以下几个步骤: 1. **检测连接状态**:心跳机制是检测连接状态的主要方式,如果...
本资料包主要关注如何在Android端集成Mina框架,并解决断线重连及多端口问题。 首先,Mina框架是Apache软件基金会的一个项目,它提供了一个事件驱动、异步的I/O模型,使得开发者可以构建高性能、高可用性的网络应用...
实现:手机安卓APP与服务器建立长连接,客户端通过心跳机制保活,并在网络切换比如4G与WiFi的切换以及网络不稳定(服务器坏掉,网卡)造成的长连接断开...采用框架:服务器端和安卓开发均采用Mina框架进行快速开发。
在Android开发中,Mina可以帮助开发者轻松地处理TCP/IP和UDP/IP通信,尤其适用于实现长连接和断线重连功能。下面将详细介绍Mina框架在Android中的应用及其核心概念。 1. **Mina框架基础** Mina源于Java平台,但在...
此外,为了保持健壮性和安全性,通常还会包含身份验证、权限控制、心跳检测和断线重连等机制。 总而言之,Mina聊天室是一个利用Apache Mina构建的实时通信应用,它展示了如何利用Mina框架处理网络通信,以及如何与...
此外,你可能还需要处理断线重连、心跳机制、错误处理等常见问题。 压缩包中的`html`文件可能是用于展示前端聊天界面的HTML和JavaScript代码,这部分通常包括创建WebSocket连接、发送和接收消息的函数,以及用户...
Mina在设计上充分考虑到了网络通信中的各种异常情况,如断线重连、心跳检测等,并提供了相应的处理机制,确保服务的稳定性和可靠性。 #### 三、Mina框架的核心组件 Mina的核心组件主要包括以下几部分: ##### 3.1...
8. **异常处理**:理解如何处理网络通信中的各种异常情况,如断线重连、错误命令响应等。 9. **性能优化**:学习如何调整Mina FTPServer的性能,例如通过缓冲区管理和线程池配置来提升服务器处理能力。 10. **安全...
同时,MINA还提供了异常处理机制,当网络异常发生时,会触发相应的事件,开发者可以据此进行错误恢复或重连操作。 在Android Studio环境下开发MINA应用,需要注意Android的多线程模型和资源管理。由于Android主线程...
7. **故障恢复策略**:对于断网情况,服务端可能需要设计重连机制,当网络恢复后,客户端能自动重新连接到服务端。 8. **代码组织**:MinaSample项目可能包含服务端启动类、IoHandler实现、配置文件等,这些都展示...
在长连接中,需要处理网络抖动、断线重连、心跳维持等问题。Mina提供了一些内置机制,如超时管理、异常处理等,帮助开发者有效地维护长连接。 ### 示例代码 在Android环境下,Mina的使用基本与Java环境相同,但...
4. **心跳机制**:为了检测和处理断线情况,消息推送系统通常会采用心跳机制。客户端定期发送心跳消息到服务器,服务器响应确认,确保连接的活跃性。 5. **并发与性能优化**:MINA的非阻塞I/O模型能够有效地处理...
代码利用mina框架实现了服务器端和客户端,客户端的登录请求、客户端向服务器端请求图片文件的功能。代码分为服务端和客户端,开发环境eclipse。服务器代码可在web项目中使用,客户端代码可以在Android开发时使用,...
同时,系统需要有良好的容错机制,如断线重连、错误恢复等,以确保服务的稳定运行。 7. **性能优化**:为了处理大量并发请求并减少延迟,Mina使用非阻塞I/O模型,避免了线程上下文切换带来的开销。此外,还可以通过...
6. **错误处理**:对于网络异常、协议错误等,应有适当的错误处理机制,包括重试、断线重连以及向用户报告错误信息。 7. **多线程设计**:为了提高性能和用户体验,邮件客户端可能需要在多个线程中并行处理任务,...
`jsch_examples`文件可能包含了JSch库的示例代码,这些代码演示了如何使用JSch进行更复杂的操作,比如处理SFTP会话中的异常、断线重连、文件重命名等。 总的来说,Java版的SFTP实现利用JSch库可以方便地进行文件的...
- **断线重连机制**:在长连接可能因为网络波动等原因断开的情况下,系统需要能够及时检测到断线,并尝试重新连接。 - **多客户端连接**:推送服务器需要能够同时处理多个客户端的连接请求,保证每个用户都能接收到...