springboot+mina框架服务端的实现(二) ------ 心跳包、自定义Session及其管理类、业务处理类、mina连接的创建

接上一节:springboot+mina框架服务端的实现(一) ------ pom依赖、mina配置类、自定义协议以及编解码器的实现

参考博客:矢落叶の博客

四、 心跳包的实现

先简单介绍下keepAlive的机制:

首先,需要搞清楚TCP keepalive是干什么用的。从名字理解就能够知道,keepalive就是用来检测一个tcp connection是否还连接正常。当一个tcpconnection建立好之后,如果双方都不发送数据的话,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完蛋了(至少从表面上看是这样)。

MINA本身提供了一个过滤器类: org.apache.mina.filter.keepalive.KeepAliveFilter ,该过滤器用于在IO空闲的时候发送并且反馈心跳包(keep-alive request/response)。
该类构造函数中参数有三个分别是:
(1)KeepAvlieMessageFactory: 该实例引用用于判断接受与发送的包是否是心跳包,以及心跳请求包的实现
(2)IdleStatus: 该过滤器所关注的空闲状态,默认认为读取空闲。 即当读取通道空闲的时候发送心跳包
(3)KeepAliveRequestTimeoutHandler: 心跳包请求后超时无反馈情

先看KeepAliveMessageFactory接口:

public interface KeepAliveMessageFactory {
   

    boolean isRequest(IoSession session, Object message);

    boolean isResponse(IoSession session, Object message);

    Object getRequest(IoSession session);

    Object getResponse(IoSession session, Object request);
}

实现这个接口:

public class KeepAliveFactoryImpl implements KeepAliveMessageFactory {
   

    static final Logger logger = LoggerFactory.getLogger(KeepAliveFactoryImpl.class);

    @Resource
    private BaseHandler keepAliveHandler;

    // 用来判断接收到的消息是不是一个心跳请求包,是就返回true[接收端使用]
    @Override
    public boolean isRequest(IoSession session, Object message) {
   
        if (message instanceof MyPack) {
   
            MyPack pack = (MyPack) message;
            if (Const.HEART_BEAT == pack.getModule()) {
   
                return true;
            }
        }
        return false;
    }

    // 用来判断接收到的消息是不是一个心跳回复包,是就返回true[发送端使用]
    @Override
    public boolean isResponse(IoSession session, Object message) {
   
        // TODO Auto-generated method stub
        return false;
    }

    // 在需要发送心跳时,用来获取一个心跳请求包[发送端使用]
    @Override
    public Object getRequest(IoSession session) {
   
        // TODO Auto-generated method stub
        return null;
    }

    // 在需要回复心跳时,用来获取一个心跳回复包[接收端使用]
    @Override
    public Object getResponse(IoSession session, Object request) {
   
        MyPack attendPack = (MyPack) request;
        if (null == session.getAttribute(Const.SESSION_KEY)) {
   
        	// 需要先进行登录
            return new MyPack(Const.AUTHEN, attendPack.getSeq(), "fail");
        }
        // 将超时次数置为0
        session.setAttribute(Const.TIME_OUT_KEY, 0);
        return new MyPack(Const.HEART_BEAT, attendPack.getSeq(), "success");
    }

}

超时后的处理放在业务处理类的sessionIdle方法中实现,稍后详细介绍

五、 自定义Session类及其管理类

方便对session会话进行管理,方便对session会话集合获取和删除

服务端接收到新的Session后,构造一个封装类,实现session 的部分方法,并额外实现方法

5.1 MySession 类

就一个处理业务的方法,使用自定义Session代替IoSession

public class MySession implements Serializable {
   

    private static final long serialVersionUID = 1L;

    // 不参与序列化
    private transient IoSession session;

    // session在本机器的ID
    private Long nid;
    // session绑定的服务ip
    private String host;
    // 访问端口
    private int port;
    // session绑定的设备
    private String account;

    public MySession() {
   
    }

    public MySession(IoSession session) {
   
        this.session = session;
        this.host = ((InetSocketAddress) session.getRemoteAddress()).getAddress().getHostAddress();
        this.port = ((InetSocketAddress) session.getRemoteAddress()).getPort();
        this.nid = session.getId();
    }

    /**
     * 将key-value自定义属性,存储到IO会话中
     */
    public void setAttribute(String key, Object value) {
   
        if (null != session) {
   
            session.setAttribute(key, value);
        }
    }

    /**
     * 从IO的会话中,获取key的value
     */
    public Object getAttribute(String key) {
   
        if (null != session) {
   
            return session.getAttribute(key);
        }
        return null;
    }

    /**
     *  在IO的会话中,判断是否存在包含key-value
     */
    public boolean containsAttribute(String key) {
   
        if (null != session) {
   
            return session.containsAttribute(key);
        }
        return false;
    }

    /**
     * 从IO的会话中,删除key
     */
    public void removeAttribute(String key) {
   
        if (null != session) {
   
            session.removeAttribute(key);
        }
    }

    /**
     *  获取IP地址
     */
    public SocketAddress getRemoteAddress() {
   
        if (null != session
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值