在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对象有四个状态:
现在整理一下实现过程:
ctx.executor().schedule(new ConnectionTerminator(ctx), 5, TimeUnit.SECONDS);
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"; } } }
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"; } } } }