前言介绍:
本案例主要介绍服务端心跳包使用
本案例心跳包主要用于服务端在读、写、读写超时内做出的相应处理。
代码拿到手后可以最好测试下,事必躬亲,无论案例如何明白也得自己测试。
环境需求:【一下内容下文提供下载】
1、Java
1.1、jdk1.7
1.2、Eclipse
2、netty-all-5.0.0.Alpha1.jar
代码部分:
ChildChannelHandler.java
import java.nio.charset.Charset; import java.util.concurrent.TimeUnit; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.Delimiters; import io.netty.handler.codec.FixedLengthFrameDecoder; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.handler.timeout.IdleStateHandler; import io.netty.util.CharsetUtil; public class ChildChannelHandler extends ChannelInitializer{ @Override protected void initChannel(SocketChannel e) throws Exception { System.out.println("报告"); System.out.println("信息:有一客户端链接到本服务端"); System.out.println("IP:" + e.localAddress().getHostName()); System.out.println("Port:" + e.localAddress().getPort()); System.out.println("报告完毕"); /** * 心跳包 * 1、readerIdleTimeSeconds 读超时时间 * 2、writerIdleTimeSeconds 写超时时间 * 3、allIdleTimeSeconds 读写超时时间 * 4、TimeUnit.SECONDS 秒[默认为秒,可以指定] */ e.pipeline().addLast(new IdleStateHandler(2, 2, 2)); // 基于换行符号解码器 e.pipeline().addLast(new LineBasedFrameDecoder(1024)); // 解码转String e.pipeline().addLast(new StringDecoder(Charset.forName("GBK"))); // 编码器 String e.pipeline().addLast(new StringEncoder(Charset.forName("GBK"))); // 处理心跳 【放在编码解码的下面,因为这个是通道有处理顺序】 e.pipeline().addLast(new MyIdleHandler()); // 在管道中添加我们自己的接收数据实现方法 e.pipeline().addLast(new MyServerHanlder()); } }
MyIdleHandler.java
import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; public class MyIdleHandler extends ChannelHandlerAdapter { @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { if (evt instanceof IdleStateEvent) { IdleStateEvent e = (IdleStateEvent) evt; if (e.state() == IdleState.READER_IDLE) { System.out.println("--- Reader Idle ---"); ctx.writeAndFlush("读取等待:客户端你在吗... ...\r\n"); // ctx.close(); } else if (e.state() == IdleState.WRITER_IDLE) { System.out.println("--- Write Idle ---"); ctx.writeAndFlush("写入等待:客户端你在吗... ...\r\n"); // ctx.close(); } else if (e.state() == IdleState.ALL_IDLE) { System.out.println("--- All_IDLE ---"); ctx.writeAndFlush("全部时间:客户端你在吗... ...\r\n"); } } } }
MyServerHanlder.java
import java.util.Date; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.bytes.ByteArrayDecoder; public class MyServerHanlder extends ChannelHandlerAdapter{ /* * channelAction * * channel 通道 * action 活跃的 * * 当客户端主动链接服务端的链接后,这个通道就是活跃的了。也就是客户端与服务端建立了通信通道并且可以传输数据 * */ public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println(ctx.channel().localAddress().toString()+" channelActive"); //通知您已经链接上客户端 String str = "您已经开启与服务端链接"+" "+ctx.channel().id()+new Date()+" "+ctx.channel().localAddress(); ctx.writeAndFlush(str); } /* * channelInactive * * channel 通道 * Inactive 不活跃的 * * 当客户端主动断开服务端的链接后,这个通道就是不活跃的。也就是说客户端与服务端的关闭了通信通道并且不可以传输数据 * */ public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println(ctx.channel().localAddress().toString()+" channelInactive"); } /* * channelRead * * channel 通道 * Read 读 * * 简而言之就是从通道中读取数据,也就是服务端接收客户端发来的数据 * 但是这个数据在不进行解码时它是ByteBuf类型的后面例子我们在介绍 * */ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { //注意此处已经不需要手工解码了 System.out.println(ctx.channel().id()+""+new Date()+" "+msg); //通知您已经链接上客户端[给客户端穿回去的数据加个换行] String str = "服务端收到:"+ctx.channel().id()+new Date()+" "+msg+"\r\n"; //发送给客户端【可以开启关闭用于测试心跳包】 ctx.writeAndFlush(str); } /* * channelReadComplete * * channel 通道 * Read 读取 * Complete 完成 * * 在通道读取完成后会在这个方法里通知,对应可以做刷新操作 * ctx.flush() * */ public void channelReadComplete(ChannelHandlerContext ctx) throws Exception { ctx.flush(); } /* * exceptionCaught * * exception 异常 * Caught 抓住 * * 抓住异常,当发生异常的时候,可以做一些相应的处理,比如打印日志、关闭链接 * */ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { ctx.close(); System.out.println("异常信息:\r\n"+cause.getMessage()); } }
NettyServer.java
package com.itstack.netty.idle; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelOption; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; public class NettyServer { public static void main(String[] args) { try { System.out.println("服务端开启等待连接... ..."); new NettyServer().bing(7397); } catch (Exception e) { e.printStackTrace(); } } public void bing(int port) throws Exception{ EventLoopGroup bossGroup = new NioEventLoopGroup(); EventLoopGroup workGroup = new NioEventLoopGroup(); try { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workGroup); b.channel(NioServerSocketChannel.class); b.option(ChannelOption.SO_BACKLOG, 1024); b.childHandler(new ChildChannelHandler()); ChannelFuture f = b.bind(port).sync(); f.channel().closeFuture().sync(); } finally { bossGroup.shutdownGracefully(); workGroup.shutdownGracefully(); } } }