1.下载jar包:点击打开链接
2.实现心跳
public class KeepAliveClientImpl implements KeepAliveMessageFactory {
public static final String REQUEST_HEART = "1";//请求
public static final String RESPONSE_HEART = "2";//响应
@Override
public boolean isRequest(IoSession ioSession, Object o) {
if (o instanceof String) {
String s = (String) o;
if (s.equals(REQUEST_HEART)) {//服务端判断是否是心跳请求
return true;
}
}
return false;
}
@Override
public boolean isResponse(IoSession ioSession, Object o) {
if (o instanceof String) {
String s = (String) o;
if (s.equals(RESPONSE_HEART)) {//客户端判断是否是心跳响应
return true;
}
}
return false;
}
@Override
public Object getRequest(IoSession ioSession) {//客户端主动发起心跳请求
return REQUEST_HEART;
}
@Override
public Object getResponse(IoSession ioSession, Object o) {//服务器响应心跳请求
return RESPONSE_HEART;
}
}
3.自定义编解码
编码
public class JavaProtobufEncoderFactory extends ProtocolEncoderAdapter {
@Override
public void encode(IoSession ioSession, Object message, ProtocolEncoderOutput out) throws Exception {
String s = (String) message;
byte[] bytes = s.getBytes("utf-8");
int length = bytes.length;
int packageLength = length + 4;//java中int类型占4个字节
System.out.println("package ==" + packageLength);
IoBuffer buffer = IoBuffer.allocate(packageLength);
buffer.putInt(length); // write header
buffer.put(bytes); // write body
buffer.flip();
out.write(buffer);
}
}
解码
public class JavaProtobufDecoderFactory extends CumulativeProtocolDecoder {
private final static Logger log = LoggerFactory
.getLogger(JavaProtobufDecoderFactory.class);
@Override
protected boolean doDecode(IoSession ioSession, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
// 如果没有接收完Header部分(4字节),直接返回false
System.out.println("byte----->in.remaining==" + in.remaining());
int packageLength = 4;
if (in.remaining() <= packageLength) {
return false;
} else {
// 标记开始位置,如果一条消息没传输完成则返回到这个位置
in.mark();
// 读取header部分,获取body长度
int bodyLength = in.getInt();
log.info("bodyLength==" + bodyLength);
// 如果body没有接收完整,直接返回false
int remaining = in.remaining();//注意:这里是从第header读完开始(假设刚开始in.remaining()==8,这里就是in.remaining()==4)
if (remaining < bodyLength) {
in.reset(); // IoBuffer position回到原来标记的地方
return false;
} else {
byte[] bodyBytes = new byte[bodyLength];
in.get(bodyBytes); // 读取body部分
String s = new String(bodyBytes);
out.write(s); // 解析出一条消息
return true;
}
}
}
}
4.创建客户端
public class MinaClient {
public static void main(String[] agb) {
NioSocketConnector connector = new NioSocketConnector();//TCP连接
//设置编码器
connector.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ProtobufFactory()));
/*
* 设置心跳
* */
KeepAliveClientImpl keepAliveClient = new KeepAliveClientImpl();
KeepAliveFilter mKeepAliveFilter = new KeepAliveFilter(keepAliveClient, IdleStatus.BOTH_IDLE, new KeepAliveRequestTimeoutHandler() {
@Override
public void keepAliveRequestTimedOut(KeepAliveFilter keepAliveFilter, IoSession ioSession) throws Exception {
System.out.println("time out --------------");
}
});
mKeepAliveFilter.setForwardEvent(true);//说明:继续调用 IoHandlerAdapter 中的 sessionIdle时间
mKeepAliveFilter.setRequestInterval(20);//说明:设置当连接的读取通道空闲的时候,心跳包请求时间间隔
mKeepAliveFilter.setRequestTimeout(5);//说明:设置心跳包请求后 等待反馈超时时间。 超过该时间后则调用KeepAliveRequestTimeoutHandler.CLOSE
connector.getFilterChain().addLast("heart", mKeepAliveFilter);
connector.setHandler(new ClientHandler());//消息回调
ConnectFuture connect = connector.connect(new InetSocketAddress("你的ip地址", 9000));//连接
connect.awaitUninterruptibly();//同步等待,session创建成功为止
connect.getSession().write("hello world");//发送字符串消息(如果实际项目是传递对象,要根据需求调整编解码)
}
public static class ClientHandler extends IoHandlerAdapter {
public ClientHandler() {
}
@Override
public void sessionOpened(IoSession session) {//通道打开
}
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {//空闲状态
super.sessionIdle(session, status);
}
@Override
public void messageSent(IoSession session, Object message) throws Exception {//消息已经发出
super.messageSent(session, message);
}
@Override
public void messageReceived(IoSession session, Object message) throws Exception {//接收到消息
super.messageReceived(session, message);
}
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {//异常
super.exceptionCaught(session, cause);
}
@Override
public void sessionClosed(IoSession session) throws Exception {//session关闭
super.sessionClosed(session);
}
@Override
public void sessionCreated(IoSession session) throws Exception {//欣创建session
super.sessionCreated(session);
}
}
}
5.创建服务端
public class MinaService {
public static void main(String[] arg) {
NioSocketAcceptor acceptor = new NioSocketAcceptor();
acceptor.getSessionConfig().setReadBufferSize(2048);//设置读取缓冲大小
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);//
acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ProtobufFactory()));
//心跳机制
KeepAliveClientImpl mKeepAliveService = new KeepAliveClientImpl();
KeepAliveFilter mKeepAliveFilter = new KeepAliveFilter(mKeepAliveService, IdleStatus.BOTH_IDLE, new KeepAliveRequestTimeoutHandler() {
@Override
public void keepAliveRequestTimedOut(KeepAliveFilter keepAliveFilter, IoSession ioSession) throws Exception {
System.out.println("time out --------------");
}
});
mKeepAliveFilter.setForwardEvent(true); //idle事件回发 当session进入idle状态的时候 依然调用handler中的idled方法
//说明:尤其 注意该句话,使用了 KeepAliveFilter之后,IoHandlerAdapter中的 sessionIdle方法默认是不会再被调用的! 所以必须加入这句话 sessionIdle才会被调用
mKeepAliveFilter.setRequestInterval(30); //本服务器为被定型心跳 即需要每30秒接受一个心跳请求 否则该连接进入空闲状态 并且发出idled方法回调
// 说明:设置心跳包请求时间间隔,其实对于被动型的心跳机制来说,设置心跳包请求间隔貌似是没有用的,因为它是不会发送心跳包的,但是它会触发 sessionIdle事件, 我们利用该方法,可以来判断客户端是否在该时间间隔内没有发心跳包,一旦 sessionIdle方法被调用,则认为 客户端丢失连接并将其踢出 。因此其中参数 heartPeriod其实就是服务器对于客户端的IDLE监控时间。
mKeepAliveFilter.setRequestTimeout(5); //超时时间 如果当前发出一个心跳请求后需要反馈 若反馈超过此事件 默认则关闭连接
acceptor.getFilterChain().addLast("heart", mKeepAliveFilter);
acceptor.setHandler(new MyIoHandler());
try {
acceptor.bind(new InetSocketAddress(9000));
} catch (IOException e) {
e.printStackTrace();
}
}
public static class MyIoHandler extends IoHandlerAdapter {
@Override
public void messageReceived(IoSession session, Object message)
throws Exception {//接收到消息
System.out.println("messageReceived==" + message.toString());
}
@Override
public void messageSent(IoSession session, Object message) throws Exception {//消息已发送
super.messageSent(session, message);
}
@Override
public void exceptionCaught(IoSession session, Throwable cause) throws Exception {
super.exceptionCaught(session, cause);
}
@Override
public void sessionCreated(IoSession session) throws Exception {
super.sessionCreated(session);
}
@Override
public void sessionOpened(IoSession session) throws Exception {
super.sessionOpened(session);
}
@Override
public void sessionClosed(IoSession session) throws Exception {
super.sessionClosed(session);
}
@Override
public void sessionIdle(IoSession session, IdleStatus status) throws Exception {
super.sessionIdle(session, status);
System.out.println("sessionIdle");
}
}
}