【Netty4 简单项目实践】六、断掉未鉴权的TCP长连接--ChannelHandelContext中的定时器用法

在TCP长连接模式下,我们需要及时释放那些未授权的TCP链接,让系统运行得更稳健一些。

首先是connect上来的TCP报文需要设置一个存活期,通过在pipleline上设置超时处理器ReadTimeoutHandler

ch.pipeline().addLast(new ReadTimeoutHandler(120));

使得一个TCP在120秒内没有收到数据就断掉。

这样做的目的是让连接者必须发TCP报文才能维持连接。

下一步在业务层对ChannelHandlerContext进行鉴权。与HTTP不同,TCP长连接的会话ChannelHandlerContext是一直存活的。只要TCP不断,每次进入处理器的ctx都是同一个对象,也是在同一个Handler对象中处理消息。基于这个考虑,可以在第一次收到TCP消息的时候启动一个定时器,等待类似鉴权的TCP消息。如果定时器超时的时候鉴权都没成功,则主动关闭这个TCP连接:ctx.close();。

在这个鉴权过程中Handler对象有四个状态:

  1. 初始态(瞬态)init
  2. 等待鉴权           waiting
  3. 鉴权成功            success
  4. 鉴权失败(瞬态) failed

在收到第一个消息之前是“1.初始态”,当收到第一个消息后进入“2.等待鉴权”,当鉴权成功的时候转为“3.鉴权成功”,当定时器超时后状态不是“3”的时候就进入“4.鉴权失败”。

由于收到第一个消息之前,Handler对象并没有实例化,而在实例化之后马上进入了状态“2”,所以状态“1”是瞬态;

由于鉴权失败之后关闭了ctx,之后Handler对象将不会再进入,所以状态“4”也可以理解为瞬态。

现在整理一下实现过程:

  1. 在Handler中设置一个域表示状态status,初始化为“init”;
  2. 收到第一个消息的时候,即read() 或者 read0()回调被触发的时候,status初始化为"waiting",在ChannelHandlerContext上启动一个5秒定时器(第一个消息,指status.equals("init"))
    ctx.executor().schedule(new ConnectionTerminator(ctx), 5, TimeUnit.SECONDS);
  3. 其中ConnectionTerminator对象是自定义的一个线程回调,这个回调唯一的作用是是当状态不为success的时候,关掉连接
    private class ConnectionTerminator implements Runnable{
            ChannelHandlerContext ctx;
            public ConnectionTerminator(ChannelHandlerContext ctx) {
                // TODO Auto-generated constructor stub
                this.ctx = ctx;
            }
            @Override
            public void run() {
                // TODO Auto-generated method stub
                if (!status.equals("success")) {
                    ctx.close();
                    status="failed";
                }
            }
        } 
  4. 在Login消息返回成功之后,把status置为"success"即可。

完整的例子如下:

第一步:加载读超时

ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast("readTimeout", new ReadTimeoutHandler(120));

第二步:处理器入口

/**
 * 以Protocol buf作为消息体的Handler
 * @author yuantao
 *
 */
public class ProtoBufHandler extends SimpleChannelInboundHandler<Protocol> {
    private String status = "init";

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Protocol msg) throws Exception {
        // TODO Auto-generated method stub
        // 放在Netty循环中跑线程
        if (status.equals("init")) {
            ctx.executor().schedule(new ConnectionTerminator(ctx), 5, TimeUnit.SECONDS);
            status = "waiting";
        }

        if (msg.hasLogin()) {
            int result = new MessageProcessor(msg, ctx).processLogin(msg.getLogin());
            if (result == 0) {
                status = "success";
            }
        }
        else {
            ctx.executor().execute(new MessageProcessor(msg, ctx));
        }
    }  

    private class ConnectionTerminator implements Runnable{
        ChannelHandlerContext ctx;
        public ConnectionTerminator(ChannelHandlerContext ctx) {
            // TODO Auto-generated constructor stub
            this.ctx = ctx;
        }
        @Override
        public void run() {
            // TODO Auto-generated method stub
            if (!status.equals("success")) {
                ctx.close();
                status = "failed";
            }
        }
    }
}

服务器运行在这种机制下的时候,需要客户端在120秒内发握手消息来维持长连接,比如30秒发一次握手。

时间: 2024-10-12 04:42:57

【Netty4 简单项目实践】六、断掉未鉴权的TCP长连接--ChannelHandelContext中的定时器用法的相关文章

Redis实践操作之—— 直播视频定时控制【TCP长连接框架(WorkerMan)+键空间通知的机制 ( Keyspace Notifications)+短信接口(API)】

一.思路梳理 同步直播视频到Redis 用户观看直播模板,点击直播按钮,检查是否有权限. 直播定时免费观看(免费观看10分钟),用户点击播放按钮开始,异步检查获取直播活动设置的免费观看时间(后台维护人员设置,Redis的hash存储信息),是否是直播. 是直播视频:判断该客户是否已经观看过了免费的20分钟时间, 没有看过,则获取该直播视频的免费时间根据活动ID,同时设置该直播视频的过期时间(只针对该用户自己哦),返回个模板,说:这个人可以观看的. 直播视频已经看过了,则不可以继续观看哦!嘻嘻..

api-gateway实践(7)鉴权场景和网关场景梳理、OAuth2待澄清问题列表

一.身份鉴权验证 1.业务请求 1.1.父元素声明了 "/GroupA/VersionB/*",子元素声明了 "/GroupA/VersionB/Cxxx",access="ROLE_XXXX" 身份识别: 有效token 无token.无效token   权限鉴别: 有权限: 无权限: 1.2.父元素声明了 "/GroupA/VersionB/*",子元素没有声明的 "/GroupA/VersionB/Dxxx&q

Intellij Idea 17.2 Servlet 简单项目实践

在网上查看了许多关于 Intellij Idea  Servlet项目开发的,但是大多是时间很久的老古董了,经过自己的不懈努力,终于实现了自己想要的功能. 这里是最新版 Intellij Idea 17.2  Tomcat 8 在这里主要记录自己做项目的过程,仅供和我一样在学习的菜鸟做一个参考. 目标: 通过Intellij Idea  实现一个 Servlet 项目. 第一,建立一个web项目: file---->new---->Project next 之后填写项目名称,我的是:myDem

5.4 交易鉴权

5.4.1 账户权限相关概念 权限 EOS采用父子分层的权限结构,低级权限(子权限)由高级权限(父权限)派生而来,父权限拥有子权限所有的能力.子权限能做的事父权限也能做,但是反过来,父权限能做的事,子权限不一定能做. owner 是最高等级权限,拥有owner权限就意味着拥有账户的所有权,我们可以把owner理解为超级管理员权限. active 是owner的子权限,主要用来发送交易.投票或者进行高级别的账户修改操作. 权重 权限拥有者在权限中的重要程度,具体以不小于1的整数表示. 阈值 执行该

第六周作业:《人月神话》对我做项目实践的启示(一)

<人月神话>这本书有两个老师都有给我们推荐,第一个老师推荐时不以为然,第二个老师也推荐时,自己感觉应该是挺重要的吧,于是去图书馆借了这本书来看,刚借回来时,总觉得时间不够.作业很多,也没来的及看,就一直搁置在了那里,直到上周,在我们的项目实践开始近三周,但进度却一直赶不上来的情况下,看到了这本书,才拿起来看.目前还没看完,先写一点儿领悟到的东西. 作者从焦油坑,提出项目失败的表现,把过去几十年的大型系统开发比作一个炼焦坑,各种团队一个个地淹没在焦油坑,他们都试图解决面对的问题,但他们都必须去了

《Java项目实践》:简单聊天程序

<Java项目实践>:简单聊天程序 由于这个简单程序,还是涉及到很多的知识点,下面我们就一点一点的来完成. 我们熟悉的QQ聊天,就有一个界面,是吧,我们自己做一个简单的聊天程序,因此我们也就需要为Client写一个界面.因此,这就是我们第一步需要完成的任务. 第一步:为Client端写一个界面 完成一个界面有两种方法,一种是使用Frame对象来完成,另外一种是继承JFrame类来完成.本项目使用第二种. 第二种继承JFrame类完成的界面的程序如下: public class ChatClie

Django项目实践4 - Django网站管理(后台管理员)

http://blog.csdn.net/pipisorry/article/details/45079751 上篇:Django项目实践3 - Django模型 Introduction 对于某一类站点, 管理界面 是基础设施中很重要的一部分. 这是以网页和有限的可信任管理者为基础的界面,它能够让你加入,编辑和删除站点内容. 常见的样例: 你能够用这个界面公布博客,后台的站点管理者用它来润色读者提交的内容,你的客户用你给他们建立的界面工具更新新闻并公布在站点上.这些都是使用管理界面的样例. 创

LVS (Linux Virtual Server)集群项目实践

LVS (LinuxVirtual Server)集群项目实践 实验目的:通过实验可以熟练规划和配置集群项目 实验环境:Red Hat Enterprise Linux Server release 6.4 实验前提:请确保实验前看过 LVS 中文站点 实验说明:本实验只是以实现负载均衡为目标,并没有考虑如共享存储等,这方面问题在以后的实验中 会添加. 实验步骤: 一.LVS 系统模型 二.LVS 调度算法 三.负载平衡方法 四.常用术语介绍 五.NAT 方式架设 六.DR方式架设 一.LVS

Hangfire项目实践

Hangfire项目实践分享 Hangfire项目实践分享 目录 Hangfire项目实践分享 目录 什么是Hangfire Hangfire基础 基于队列的任务处理(Fire-and-forget jobs) 延迟任务执行(Delayed jobs) 定时任务执行(Recurring jobs) 延续性任务执行(Continuations) 与quartz.net对比 Hangfire扩展 Hangfire Dashborad日志查看 Hangfire Dashborad授权 IOC容器之Au