Apache Mina心跳及断线重连
原理
客户端
package com.cnblogs.javalouvre;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.future.ConnectFuture;
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;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.keepalive.KeepAliveFilter;
import org.apache.mina.filter.keepalive.KeepAliveMessageFactory;
import org.apache.mina.filter.keepalive.KeepAliveRequestTimeoutHandler;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketConnector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.util.concurrent.TimeUnit;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.mina.filter.codec.textline.LineDelimiter.WINDOWS;
public class Client {
private static final Logger log = LoggerFactory.getLogger(Client.class);
private static final String HOST = "127.0.0.1";
private static final int PORT = 5678;
public static void main(String[] args) {
final NioSocketConnector connector = new NioSocketConnector();
// 获取socket会话配置
final SocketSessionConfig sessionConfig = connector.getSessionConfig();
sessionConfig.setKeepAlive(Boolean.TRUE); // 长链接
sessionConfig.setTcpNoDelay(Boolean.TRUE); // 低延迟
// 过滤器链
final DefaultIoFilterChainBuilder filterChain = connector.getFilterChain();
// 编码、解码过滤器
filterChain.addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(UTF_8, WINDOWS.getValue(), WINDOWS.getValue())));
// 日志过滤器
filterChain.addLast("logger", new LoggingFilter());
// 心跳过滤器
final KeepAliveFilter keepAliveFilter = new KeepAliveFilter(new KeepAliveMessageFactory() {
@Override
public boolean isRequest(IoSession session, Object message) {
return true;
}
@Override
public boolean isResponse(IoSession session, Object message) {
return false;
}
@Override
public Object getRequest(IoSession session) {
return "0x10";
}
@Override
public Object getResponse(IoSession session, Object request) {
return null;
}
}, IdleStatus.WRITER_IDLE, KeepAliveRequestTimeoutHandler.NOOP, 10, 5);
filterChain.addLast("heartBeat", keepAliveFilter);
// 事件处理
connector.setHandler(new NoopIoHandlerAdapter());
// 添加重连监听
connector.addListener(new ReconnectIoServiceListener(connector));
final ConnectFuture future = connector.connect(new InetSocketAddress(HOST, PORT));
// 阻塞直到和服务端连接成功
future.awaitUninterruptibly();
// connector.dispose();
}
}
class ReconnectIoServiceListener implements IoServiceListener {
private static final Logger log = LoggerFactory.getLogger(ReconnectIoServiceListener.class);
private final NioSocketConnector connector;
public ReconnectIoServiceListener(NioSocketConnector connector) {
this.connector = connector;
}
@Override
public void serviceActivated(IoService service) throws Exception {
}
@Override
public void serviceIdle(IoService service, IdleStatus idleStatus) throws Exception {
}
@Override
public void serviceDeactivated(IoService service) throws Exception {
}
@Override
public void sessionCreated(IoSession session) throws Exception {
}
@Override
public void sessionClosed(IoSession session) throws Exception {
}
@Override
public void sessionDestroyed(IoSession session) throws Exception {
while (true) {
try {
TimeUnit.SECONDS.sleep(3);
ConnectFuture future = connector.connect();
future.awaitUninterruptibly(); // 等待连接创建成功
session = future.getSession(); // 获取会话
if (session.isConnected()) {
log.info("断线重连[" + connector.getDefaultRemoteAddress().getHostName() + ":" + connector.getDefaultRemoteAddress().getPort() + "]成功");
break;
}
} catch (Exception e) {
log.info("重连服务器登录失败,3秒再连接一次: {}", e.getMessage());
}
}
}
}
服务端
package com.cnblogs.javalouvre;
import org.apache.mina.core.filterchain.DefaultIoFilterChainBuilder;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.SocketSessionConfig;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.InetSocketAddress;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.apache.mina.filter.codec.textline.LineDelimiter.WINDOWS;
public class Server {
private static final Logger log = LoggerFactory.getLogger(Server.class);
private static final int PORT = 5678;
private void bind() {
final NioSocketAcceptor acceptor = new NioSocketAcceptor();
// 过滤器链
final DefaultIoFilterChainBuilder filterChain = acceptor.getFilterChain();
// 编码、解码过滤器
filterChain.addLast("codec", new ProtocolCodecFilter(new TextLineCodecFactory(UTF_8, WINDOWS.getValue(), WINDOWS.getValue())));
// 日志过滤器
filterChain.addLast("logger", new LoggingFilter());
// 获取socket会话配置
final SocketSessionConfig sessionConfig = acceptor.getSessionConfig();
// 设置缓存大小
sessionConfig.setReadBufferSize(1024);
// 设置读空闲时间10秒
sessionConfig.setIdleTime(IdleStatus.READER_IDLE, 10);
// 事件处理
acceptor.setHandler(new NoopIoHandlerAdapter());
try {
// 绑定端口
acceptor.bind(new InetSocketAddress(PORT));
} catch (IOException e) {
e.printStackTrace();
}
log.info("端口 {} 启动成功", PORT);
}
public static void main(String[] args) {
new Server().bind();
}
}
消息处理
package com.cnblogs.javalouvre;
import org.apache.mina.core.service.IoHandlerAdapter;
public class NoopIoHandlerAdapter extends IoHandlerAdapter {
}
-----------------------------------------------------------------------------------------------------------
薔薇猛虎皆成個性,陽光雨露俱是天恩!
薔薇猛虎皆成個性,陽光雨露俱是天恩!