### 前言

在之前的文章已经讲过了`mina`长连接的相关知识,大家感兴趣的话,可以参看文章

为了让长链接实现在线保活,时刻保持其连接活性,接下来我们就讲讲`mina`长连接的`心跳机制`

今天涉及以下内容:

1. 心跳机制种类

2. 心跳机制在客户端和服务端涉及的类

3. 心跳机制在客户端和服务端的实现

4. 效果图个项目结构图

先来波效果图

![客户端心跳日志.png](/contentImages/image/20200630/wIhGqqmkCrxBKDyTMvT.png)

![服务端心跳日志.png](/contentImages/image/20200630/qyStzmvIiSt511vEMKe.png)

#### 一.心跳机制种类

`mina`提供一个过滤器类`org.apache.mina.filter.keepalive.KeepAliveMessageFactory`,此类具备在IO空闲的时候发送并且反馈心跳包。

`KeepAliveMessageFactory`接口主要有以下几个方法:

```
boolean isRequest(IoSession ioSession, Object o)
boolean isResponse(IoSession ioSession, Object o)
Object getRequest(IoSession ioSession)
Object getResponse(IoSession ioSession, Object o)
```

心跳机制主要分为下面几类:

##### 1.1 active 活跃型

读取通道空闲时发送心跳请求,一旦心跳请求被发出,若在`keepAliveRequestTimeout`内未收到心跳反馈,`KeepAliveRequestTimeoutHandler`将会被调用.心跳包被接收后,心跳反馈会立刻发出。

在此心跳机制下,`KeepAliveMessageFactory`类中的`getRequest(IoSession ioSession)`与`getResponse(IoSession ioSession, Object o) `必须返回非空

##### 1.2 semi-active 半活跃型

读取通道空闲时发送心跳请求。发送心跳请求单不关注心跳反馈,当一个心跳请求包被接收后,心跳反馈立马发出。

在此机制下,`KeepAliveMessageFactory`类中的`getRequest(IoSession ioSession)`与`getResponse(IoSession ioSession, Object o) `必须返回非空。并且心跳包请求后超时无反馈的处理机制设置为`KeepAliveRequestTimeoutHandler.NOOP`(不做任何处理), `KeepAliveRequestTimeoutHandler.LOG`(只输出警告信息不做其他处理)

##### 1.3 passive 被动型

当前IO不希望主动发送心跳请求,但是当接受到一个心跳请求后,那么该心跳反馈也会立即发出。

在此机制下, `KeepAliveMessageFactory`类中的`getRequest(IoSession ioSession)`必须反馈`null`,`getResponse(IoSession ioSession, Object o) `必须返回非空。

##### 1.4 deaf speaker 聋子型

当前IO会主动发送心跳请求,但是不想发送任何心跳反馈。

在此机制下,`KeepAliveMessageFactory`类中的`getRequest(IoSession ioSession)`与`getResponse(IoSession ioSession, Object o) `必须返回`null`。并且`KeepAliveRequestTimeoutHandler`设置为`DEAF_SPEAKER`

##### 1.5 sient-listener 持续监听型

既不想发送心跳请求也不想发送心跳反馈。

在此机制下,`KeepAliveMessageFactory`类中的`getRequest(IoSession ioSession)`与`getResponse(IoSession ioSession, Object o) `必须返回`null`。

心跳包请求超时后的处理机制:接口`KeepAliveRequestTimeoutHandler` ,一般该处理主要是针对能够发送心跳请求的心跳机制。

1.CLOSE:关闭连接

2,LOG:输出警告信息

3,NOOP:不做任何处理

4,EXCEPTION:抛出异常

5,DEAF_SPEAKER:一个特殊的处理,停止当前过滤器对对心跳反馈监听,因此让过滤器丢失请求超时的侦测功能。(让其变成聋子)

6,keepAliveRequestTimeout(KeepAliveFilter filter, IoSession session);   自定义处理

#### 二.心跳机制在客户端和服务端涉及的类

以被动型(`passive 被动型`)为例,则客户端涉及到的心跳类有:

ClientHeartBeatFactory:实现`KeepAliveMessageFactory`接口,处理客户端的心跳包收发逻辑

服务端涉及到了类有:

ServerHeartBeatFactory:实现`KeepAliveMessageFactory`接口,处理服务端的心跳包收发逻辑

#### 三.心跳机制在客户端和服务端的实现

客户端连接设置心跳机制如下:

```
public class TempActivity extends AppCompatActivity implements View.OnClickListener{
private TextView mTv;
private Button mBtn1;
private Button mBtn2;
//声明tcp客户端操作对象和配置对象
private TmClientConfig mConfig;
private TmClientManager mTmClientManager;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_temp);
//初始化控件
initView();
//初始化数据
initData();
//设置监听
setListener();
}
/**初始化控件**/
private void initView(){
mTv=findViewById(R.id.tv);
mBtn1=findViewById(R.id.btn1);
mBtn2=findViewById(R.id.btn2);
}
/**初始化数据**/
private void initData() {
//初始化tcp配置
mConfig = new TmClientConfig.Builder(TempActivity.this)
.setIp("192.168.50.152")//设置服务端ip
.setPort(9124)//设置端口(0-65535)之间
// .setConnectTimeOut(30000)//设置连接超时时间,单位毫秒,默认30000,即30秒
// .setCharsetName("UTF-8")//设置字符集,默认为 “UTF-8”
// .setReadBufferSize(2048)//设置接收数据缓存区,默认2048
.setHeartBeat(true)//设置是否开启心跳机制,默认false,即关闭
.setHbDelayTime(10)//设置心跳包发送时间间隔,默认10秒(设置开启心跳机制后才生效)
.setHbBackTime(5)//设置心跳包接收时间间隔,默认5秒(设置开启心跳机制后才生效)
//设置发送和接收心跳包数据的处理(设置开启心跳机制后才生效)
.setChbListener(new ClientHeartBeatFactory.OnClientHeartBeatListener() {
@Override
public boolean isResponse(IoSession ioSession, Object obj) {
//客户端关注请求反馈,因此判断mesaage是否是反馈包
String message=obj.toString();
if("MinaServer".equals(message)){
return true;
}
return false;
}
@Override
public Object getRequest(IoSession ioSession) {
//获取心跳请求包 non-null
return "MinaClient";
}
})
//设置mina客户端接收数据监听
.setCmrListener(new TmClientHandler.OnMessageReceivedListener() {
@Override
public void messageReceived(IoSession session, Object message) {
//接收服务端消息
//......
LogUtil.i("======我是服务端返回消息==message=" + message.toString());
runOnUiThread(new Runnable() {
@Override
public void run() {
mTv.setText(message.toString());
}
});
}
}).build();
//初始化操作对象
mTmClientManager = new TmClientManager(mConfig);
}
/**设置监听**/
private void setListener(){
mBtn1.setOnClickListener(this);
mBtn2.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn1://测试1
LogUtil.i("=======测试====1===");
test();
break;
case R.id.btn2://测试2
String result="我是中国人啊你是谁,我是亚瑟,你知道么,大家好一切都号";
String result1="abc";
//给服务端发送消息
SessionManager.getInstance().writeToServer(result1);
break;
default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//断开连接
mTmClientManager.disConnect();
}
/**测试**/
private void test(){
ToastUtil.shortShow("准备连接啊");
new Thread(new Runnable() {
@Override
public void run() {
//连接服务端
mTmClientManager.connect();
}
}).start();
}
}
```

服务端设置心跳机制调用代码如下:

```
public class TestJava {
public static void main(String[] args) {
TmServerManager tmServerManager = new TmServerManager();
tmServerManager.setPort(9124)
.setCharsetName("UTF-8")//设置字符编码集,若不设置则默认 UTF-8
.setReadBufferSize(2048)//设置接收缓存区大小,不设置的话默认为 2048
.setIdleTime(10)//设置服务回到空闲状态时间间隔,不设置则默认10秒
.setHeartBeat(true)//是否开启心跳机制,默认不开启
.setHbDelayTime(10)//设置接收心跳时间间隔(单位秒),若不设置则默认10秒(心跳开启生效)
//心跳反馈(心跳开启生效)
.setOnServerHeartBeatListener(new ServerHeartBeatFactory.OnServerHeartBeatListener() {
@Override
public boolean isResponse(IoSession ioSession, Object obj) {
//判断是否心跳请求包 是的话返回true
String message = obj.toString();
if ("MinaClient".equals(message)) {
return true;
}
return false;
}
@Override
public Object getRequest(IoSession ioSession) {
//根据心跳请求request 反回一个心跳反馈消息 non-null
return "MinaServer";
}
})
//通讯应答
.setOnTmsHandlerListener(new TmServerHandler.OnTmsHandlerListener() {
@Override
public String messageHandler(IoSession session, Object message) {
System.out.println("======我是收到消息====message="+message.toString());
String response=message.toString();
if(response!=null){
//服务端做出应答
return "我是服务端的亚瑟";
}
return null;
}
}).start();//启动服务
}
}
```

#### 四.效果图个项目结构图

![客户端心跳日志.png](/contentImages/image/20200630/wIhGqqmkCrxBKDyTMvT.png)

![服务端心跳日志.png](/contentImages/image/20200630/qyStzmvIiSt511vEMKe.png)

![客户端项目结构图.png](/contentImages/image/20200630/NlsUvhpkP8hknvTZJOn.png)

![服务端项目结构图.png](/contentImages/image/20200630/sGb8LJkKC2GqLrehsYX.png)

ok,今天内容就到这里了,谢谢大家。