JavaFX+NIO聊天室第三篇心跳连接

设计思路

因为网络的不可靠性, 有可能在 TCP 保持长连接的过程中, 由于某些突发情况, 例如网线被拔出, 突然掉电等,会造成服务器和客户端的连接中断. 在这些突发情况下, 如果恰好服务器和客户端之间没有交互的话, 那么它们是不能在短时间内发现对方已经掉线的。
心跳机制即可解决此类问题。

如何实现心跳机制?

两种方式实现心跳机制:
1.使用 TCP 协议层面的 keepalive 机制.
2.在应用层上实现自定义的心跳机制.

协议层面的心跳连接

TCP协议中有长连接和短连接之分。长连接的环境下,进行一次数据交互后,很长一段时间内无数据交互时,客户端可能意外断电、死机、崩溃、重启,还是中间路由网络无故断开,这些TCP连接并未来得及正常释放,那么,连接的另一方并不知道对端的情况,它会一直维护这个连接,长时间的积累会导致非常多的半打开连接,造成端系统资源的消耗和浪费,且有可能导致在一个无效的数据链路层面发送业务数据,结果就是发送失败。所以服务器端要做到快速感知失败,减少无效链接操作,这就有了TCP的Keepalive(保活探测)机制。

HTTP1.0协议不支持长连接,从HTTP1.1协议以后,连接默认都是长连接。HTTP分为长连接和短连接,其实本质上是说的TCP连接。多次HTTP请求(这些请求包括请求网页内容,CSS文件,JS文件,图片等等),其实使用的都是一个TCP连接。
当一个 TCP 连接建立之后,启用 TCP Keepalive 的一端便会启动一个计时器,当这个计时器数值到达 0 之后,会发送一个探测包,如果没有收到响应,那么就会关闭该连接。

java socket编程中有个keepalive选项,看到这个选项经常会误解为长连接,不设置则为短连接,实则不然。socket连接建立之后,只要双方均未主动关闭连接,那这个连接就是会一直保持的,就是持久的连接。keepalive只是为了防止连接的双方发生意外而通知不到对方,导致一方还持有连接,占用资源。
其实这个选项的意思是TCP连接空闲时是否需要向对方发送探测包,实际上是依赖于底层的TCP模块实现的,java中只能设置是否开启,不能设置其详细参数,只能依赖于系统配置。所以我们能知道,只要不主动断开连接,Socket就是长连接。

TCP协议层面的心跳连接的缺点

虽然在 TCP 协议层面上, 提供了 keepalive 保活机制, 但是使用它有几个缺点:
它不是 TCP 的标准协议, 并且是默认关闭的.
TCP keepalive 机制依赖于操作系统的实现, 默认的 keepalive 心跳时间是 两个小时, 并且对 keepalive 的修改需要系统调用(或者修改系统配置), 灵活性不够.
TCP keepalive 与 TCP 协议绑定, 因此如果需要更换为 UDP 协议时, keepalive 机制就失效了.

应用层面的心跳连接

由于在长连接的场景下,客户端和服务端并不是一直处于通信状态,如果双方长期没有沟通则双方都不清楚对方目前的状态;所以需要发送一段很小的报文告诉对方“我还活着”。同时还有另外几个目的:
1.服务端检测到某个客户端迟迟没有心跳过来可以主动关闭通道,让它下线。
2.客户端检测到某个服务端迟迟没有响应心跳也能重连获取一个新的连接。

Netty实现心跳检测的思路:

1.客户端成功连接服务端。
2.在客户端中的ChannelPipeline中加入IdleStateHandler,设置写事件触发时间为5s.
3.客户端超过5s未写数据,触发写事件,向服务端发送心跳包
4.同样,服务端要对心跳包做出响应
5.超过三次,1过0s服务端都会收到来自客户端的心跳信息,服务端可以认为客户端挂了,可以close链路。
6.客户端恢复正常,发现链路已断,重新连接服务端。

客户端

1.首先我们需要实现一个类,该类按照给定的时间,周期性的发送心跳消息。其次这个类,在我们收到消息或发送消息的时候不会发送消息,需要进行判断。
2.我们需要得到一个阀值,超过多久服务端没有回复,判断为服务端挂掉,需要重连。

我们实现HertBeator类,默认五秒一发心跳包。若是有发送或者接收到消息,我们就更新lastActionTime时间。HertBeator类发现lastActionTime与当前时间差值低于5S时,会继续让线程睡眠够5S,不会发送心跳包。当等待时间超过阀值没有回复,则进入判断,超过一定次数就进行重连。另外就是当我们发送心跳包失败时,我们也直接重连。

@AllArgsConstructor
@NoArgsConstructor
@Data
@Builder
public class HertBeator implements Runnable{
   

    private String username;

    private int noRec=1;

    // 没有收到服务端心跳次数的阀值
    private  int MAX_UN_REC_PING_TIMES = 3;

    // 心跳数据时间间隔(单位秒)
    private int READ_WAIT_SECONDS=5;

    private int Threshold_SECONDS=10;

    private volatile  boolean isShutdown=false;

    //上一次接收或者发送聊天消息或者接收到心跳包时更新
    private volatile LocalDateTime lastActionTime=LocalDateTime.now();

    private Reactor reactor;

    public HertBeator(Reactor reactor,String username){
   
        this.reactor=reactor;
        this.username=username;
    }

    public void updateActionTime(){
   
        lastActionTime=LocalDateTime.now()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值