mina 心跳机制
2016-09-26 11:22
295 查看
http://wandejun1012.iteye.com/blog/2065941
MINA自带了对心跳协议的支持,可以对心跳做出细致的配置,本文在次基础上实现了server端对client端的心跳检测。
在开始之前先简单介绍下keepAlive的机制:
首先,需要搞清楚TCP keepalive是干什么用的。从名字理解就能够知道,keepalive就是用来检测一个tcp connection是否还连接正常。当一个tcp connection建立好之后,如果双方都不发送数据的话,tcp协议本身是不会发送其它的任何数据的,也就是说,在一个idle的connection上,两个socket之间不产生任何的数据交换。从另一个方面讲,当一个connection建立之后,链接双方可以长时间的不发送任何数据,比如几天,几星期甚至几个月,但该connection仍然存在。
所以,这就可能出现一个问题。举例来说,server和client建立了一个connection,server负责接收client的request。当connection建立好之后,client由于某种原因机器停机了。但server端并不知道,所以server就会一直监听着这个connection,但其实这个connection已经失效了。
keepalive就是为这样的场景准备的。当把一个socket设置成了keepalive,那么这个socket空闲一段时间后,它就会向对方发送数据来确认对方仍然存在。放在上面的例子中,如果client停机了,那么server所发送的keepalive数据就不会有response,这样server就能够确认client完蛋了(至少从表面上看是这样)。
具体的源代码如下:
Server.java
Java代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import org.apache.mina.core.service.IoAcceptor;
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.keepalive.KeepAliveFilter;
import org.apache.mina.filter.keepalive.KeepAliveMessageFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Server {
private static final int PORT = 9123;
/** 30秒后超时 */
private static final int IDELTIMEOUT = 30;
/** 15秒发送一次心跳包 */
private static final int HEARTBEATRATE = 15;
/** 心跳包内容 */
private static final String HEARTBEATREQUEST = "0x11";
private static final String HEARTBEATRESPONSE = "0x12";
private static final Logger LOG = LoggerFactory.getLogger(Server.class);
public static void main(String[] args) throws IOException {
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getSessionConfig().setReadBufferSize(1024);
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE,
IDELTIMEOUT);
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
acceptor.getFilterChain().addLast(
"codec",
new ProtocolCodecFilter(new TextLineCodecFactory()));
KeepAliveMessageFactory heartBeatFactory = new KeepAliveMessageFactoryImpl();
//下面注释掉的是自定义Handler方式
// KeepAliveRequestTimeoutHandler heartBeatHandler = new
// KeepAliveRequestTimeoutHandlerImpl();
// KeepAliveFilter heartBeat = new KeepAliveFilter(heartBeatFactory,
// IdleStatus.BOTH_IDLE, heartBeatHandler);
KeepAliveFilter heartBeat = new KeepAliveFilter(heartBeatFactory,
IdleStatus.BOTH_IDLE);
//设置是否forward到下一个filter
heartBeat.setForwardEvent(true);
//设置心跳频率
heartBeat.setRequestInterval(HEARTBEATRATE);
acceptor.getFilterChain().addLast("heartbeat", heartBeat);
acceptor.setHandler(new MyIoHandler());
acceptor.bind(new InetSocketAddress(PORT));
System.out.println("Server started on port: " + PORT);
}
/**
* @ClassName KeepAliveMessageFactoryImpl
* @Description 内部类,实现KeepAliveMessageFactory(心跳工厂)
* @author cruise
*
*/
private static class KeepAliveMessageFactoryImpl implements
KeepAliveMessageFactory {
@Override
public boolean isRequest(IoSession session, Object message) {
LOG.info("请求心跳包信息: " + message);
if (message.equals(HEARTBEATREQUEST))
return true;
return false;
}
@Override
public boolean isResponse(IoSession session, Object message) {
// LOG.info("响应心跳包信息: " + message);
// if(message.equals(HEARTBEATRESPONSE))
// return true;
return false;
}
@Override
public Object getRequest(IoSession session) {
LOG.info("请求预设信息: " + HEARTBEATREQUEST);
/** 返回预设语句 */
return HEARTBEATREQUEST;
}
@Override
public Object getResponse(IoSession session, Object request) {
LOG.info("响应预设信息: " + HEARTBEATRESPONSE);
/** 返回预设语句 */
return HEARTBEATRESPONSE;
// return null;
}
}
/**
* 对应上面的注释
* KeepAliveFilter(heartBeatFactory,IdleStatus.BOTH_IDLE,heartBeatHandler)
* 心跳超时处理
* KeepAliveFilter 在没有收到心跳消息的响应时,会报告给的KeepAliveRequestTimeoutHandler。
* 默认的处理是 KeepAliveRequestTimeoutHandler.CLOSE
* (即如果不给handler参数,则会使用默认的从而Close这个Session)
* @author cruise
*
*/
// private static class KeepAliveRequestTimeoutHandlerImpl implements
// KeepAliveRequestTimeoutHandler {
//
//
// @Override
// public void keepAliveRequestTimedOut(KeepAliveFilter filter,
// IoSession session) throws Exception {
// Server.LOG.info("心跳超时!");
// }
//
// }
}
MyIoHandler.java
Java代码
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyIoHandler extends IoHandlerAdapter{
private final static Logger log = LoggerFactory
.getLogger(MyIoHandler.class);
@Override
public void sessionOpened(IoSession session) throws Exception {
}
@Override
public void sessionClosed(IoSession session) throws Exception {
}
@Override
public void messageReceived(IoSession session, Object message)
throws Exception {
String ip = session.getRemoteAddress().toString();
log.info("===> Message From " + ip + " : " + message);
}
}
启动Server后,运行telnet客户端连接到Server端,便可以测试心跳;
测试结果如下图:
MINA自带了对心跳协议的支持,可以对心跳做出细致的配置,本文在次基础上实现了server端对client端的心跳检测。
在开始之前先简单介绍下keepAlive的机制:
首先,需要搞清楚TCP keepalive是干什么用的。从名字理解就能够知道,keepalive就是用来检测一个tcp connection是否还连接正常。当一个tcp connection建立好之后,如果双方都不发送数据的话,tcp协议本身是不会发送其它的任何数据的,也就是说,在一个idle的connection上,两个socket之间不产生任何的数据交换。从另一个方面讲,当一个connection建立之后,链接双方可以长时间的不发送任何数据,比如几天,几星期甚至几个月,但该connection仍然存在。
所以,这就可能出现一个问题。举例来说,server和client建立了一个connection,server负责接收client的request。当connection建立好之后,client由于某种原因机器停机了。但server端并不知道,所以server就会一直监听着这个connection,但其实这个connection已经失效了。
keepalive就是为这样的场景准备的。当把一个socket设置成了keepalive,那么这个socket空闲一段时间后,它就会向对方发送数据来确认对方仍然存在。放在上面的例子中,如果client停机了,那么server所发送的keepalive数据就不会有response,这样server就能够确认client完蛋了(至少从表面上看是这样)。
具体的源代码如下:
Server.java
Java代码
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;
import org.apache.mina.core.service.IoAcceptor;
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.keepalive.KeepAliveFilter;
import org.apache.mina.filter.keepalive.KeepAliveMessageFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Server {
private static final int PORT = 9123;
/** 30秒后超时 */
private static final int IDELTIMEOUT = 30;
/** 15秒发送一次心跳包 */
private static final int HEARTBEATRATE = 15;
/** 心跳包内容 */
private static final String HEARTBEATREQUEST = "0x11";
private static final String HEARTBEATRESPONSE = "0x12";
private static final Logger LOG = LoggerFactory.getLogger(Server.class);
public static void main(String[] args) throws IOException {
IoAcceptor acceptor = new NioSocketAcceptor();
acceptor.getSessionConfig().setReadBufferSize(1024);
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE,
IDELTIMEOUT);
acceptor.getFilterChain().addLast("logger", new LoggingFilter());
acceptor.getFilterChain().addLast(
"codec",
new ProtocolCodecFilter(new TextLineCodecFactory()));
KeepAliveMessageFactory heartBeatFactory = new KeepAliveMessageFactoryImpl();
//下面注释掉的是自定义Handler方式
// KeepAliveRequestTimeoutHandler heartBeatHandler = new
// KeepAliveRequestTimeoutHandlerImpl();
// KeepAliveFilter heartBeat = new KeepAliveFilter(heartBeatFactory,
// IdleStatus.BOTH_IDLE, heartBeatHandler);
KeepAliveFilter heartBeat = new KeepAliveFilter(heartBeatFactory,
IdleStatus.BOTH_IDLE);
//设置是否forward到下一个filter
heartBeat.setForwardEvent(true);
//设置心跳频率
heartBeat.setRequestInterval(HEARTBEATRATE);
acceptor.getFilterChain().addLast("heartbeat", heartBeat);
acceptor.setHandler(new MyIoHandler());
acceptor.bind(new InetSocketAddress(PORT));
System.out.println("Server started on port: " + PORT);
}
/**
* @ClassName KeepAliveMessageFactoryImpl
* @Description 内部类,实现KeepAliveMessageFactory(心跳工厂)
* @author cruise
*
*/
private static class KeepAliveMessageFactoryImpl implements
KeepAliveMessageFactory {
@Override
public boolean isRequest(IoSession session, Object message) {
LOG.info("请求心跳包信息: " + message);
if (message.equals(HEARTBEATREQUEST))
return true;
return false;
}
@Override
public boolean isResponse(IoSession session, Object message) {
// LOG.info("响应心跳包信息: " + message);
// if(message.equals(HEARTBEATRESPONSE))
// return true;
return false;
}
@Override
public Object getRequest(IoSession session) {
LOG.info("请求预设信息: " + HEARTBEATREQUEST);
/** 返回预设语句 */
return HEARTBEATREQUEST;
}
@Override
public Object getResponse(IoSession session, Object request) {
LOG.info("响应预设信息: " + HEARTBEATRESPONSE);
/** 返回预设语句 */
return HEARTBEATRESPONSE;
// return null;
}
}
/**
* 对应上面的注释
* KeepAliveFilter(heartBeatFactory,IdleStatus.BOTH_IDLE,heartBeatHandler)
* 心跳超时处理
* KeepAliveFilter 在没有收到心跳消息的响应时,会报告给的KeepAliveRequestTimeoutHandler。
* 默认的处理是 KeepAliveRequestTimeoutHandler.CLOSE
* (即如果不给handler参数,则会使用默认的从而Close这个Session)
* @author cruise
*
*/
// private static class KeepAliveRequestTimeoutHandlerImpl implements
// KeepAliveRequestTimeoutHandler {
//
//
// @Override
// public void keepAliveRequestTimedOut(KeepAliveFilter filter,
// IoSession session) throws Exception {
// Server.LOG.info("心跳超时!");
// }
//
// }
}
MyIoHandler.java
Java代码
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IoSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyIoHandler extends IoHandlerAdapter{
private final static Logger log = LoggerFactory
.getLogger(MyIoHandler.class);
@Override
public void sessionOpened(IoSession session) throws Exception {
}
@Override
public void sessionClosed(IoSession session) throws Exception {
}
@Override
public void messageReceived(IoSession session, Object message)
throws Exception {
String ip = session.getRemoteAddress().toString();
log.info("===> Message From " + ip + " : " + message);
}
}
启动Server后,运行telnet客户端连接到Server端,便可以测试心跳;
测试结果如下图:
相关文章推荐
- hive的统计函数
- C++通过mysql的c api和通过mysql的Connector C++ 1.1.3操作mysql的两种方式
- 关于div中img,span垂直居中的问题
- android webView 和js交互中需要注意的几点
- JS之值类型与引用类型,及数据类型间的转换
- 第26周-window程序设计(基础篇)-第5章(图形基础)-关于颜色
- Window下搭建ESP8266编译环境
- Android第一行代码学习笔记八----网络技术
- iOS单例详解
- fir.im Weekly - 关于 iOS10 适配、开发、推送的一切
- C#窗体布局方式详解
- iOS10中UIScrollView新增refreshControl属性
- 四种浏览器对 Document.body 的 ClientHeight、OffsetHeight 和 ScrollHeight 的解释
- 2.素数对猜想
- Vuejs(14)——在v-for中,利用index来对第一项添加class
- oracle rac 和单机环境下各种不同
- 如何实现跑马灯效果
- GET,POST,PUT,DELETE的区别
- 5分钟 搞定UIButton的文本与图片的布局
- Git快速入门