Netty4 学习笔记之二:客户端与服务端心跳 demo
来源:互联网 发布:入骨相思知不知微盘 编辑:程序博客网 时间:2024/06/14 16:42
前言
在上一篇Netty demo 中,了解了Netty中的客户端和服务端之间的通信。这篇则介绍Netty中的心跳。
之前在Mina 中心跳的使用是通过继承 KeepAliveMessageFactory 心跳工厂类而实现的。而在Netty中,提供IdleStateHandler 类,可以实现对三种心跳的检测,分别是readerIdleTime、writerIdleTime和allIdleTime。
三个的参数解释如下:
1)readerIdleTime:为读超时时间(即测试端一定时间内未接受到被测试端消息);
2)writerIdleTime:为写超时时间(即测试端一定时间内向被测试端发送消息)
3)allIdleTime:所有类型的超时时间;
所以在channelPipeline中加入IdleStateHandler,我们在handler中提示的是5秒读,所以我们服务端的配置的是:
ph.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS));
因为服务端必须5秒接受一次心跳请求, 那么客户端的配置:
ph.addLast( new IdleStateHandler(0, 4, 0, TimeUnit.SECONDS));
userEventTriggered是Netty 处理心跳超时事件,在IdleStateHandler设置超时时间,如果达到了,就会直接调用该方法。如果没有超时则不调用。我们重写该方法的话,就可以自行进行相关的业务逻辑处理了。
完整的代码如下:
服务端:
服务端与之前的demo相比没有什么变化。
import io.netty.bootstrap.ServerBootstrap;import io.netty.channel.ChannelFuture;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioServerSocketChannel;/** * * Title: NettyServer* Description: Netty服务端* Version:1.0.0 * @author pancm* @date 2017年10月8日 */public class NettyServer { private static final int port = 9876; //设置服务端端口 private static EventLoopGroup group = new NioEventLoopGroup(); // 通过nio方式来接收连接和处理连接 private static ServerBootstrap b = new ServerBootstrap(); /** * Netty创建全部都是实现自AbstractBootstrap。 * 客户端的是Bootstrap,服务端的则是 ServerBootstrap。 **/ public static void main(String[] args) throws InterruptedException { try { b.group(group); b.channel(NioServerSocketChannel.class); b.childHandler(new NettyServerFilter()); //设置过滤器 // 服务器绑定端口监听 ChannelFuture f = b.bind(port).sync(); System.out.println("服务端启动成功,端口是:"+port); // 监听服务器关闭监听 f.channel().closeFuture().sync(); } finally { group.shutdownGracefully(); //关闭EventLoopGroup,释放掉所有资源包括创建的线程 } }}
服务端业务逻辑
业务逻辑这块,因为要重写userEventTriggered,所以继承ChannelInboundHandlerAdapter 。
import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import io.netty.handler.timeout.IdleState;import io.netty.handler.timeout.IdleStateEvent;/** * * Title: HelloServerHandler* Description: 服务端业务逻辑* Version:1.0.0 * @author pancm* @date 2017年10月8日 */public class NettyServerHandler extends ChannelInboundHandlerAdapter { /** 空闲次数 */ private int idle_count =1; /** 发送次数 */ private int count = 1; /** * 超时处理 * 如果5秒没有接受客户端的心跳,就触发; * 如果超过两次,则直接关闭; */ @Override public void userEventTriggered(ChannelHandlerContext ctx, Object obj) throws Exception { if (obj instanceof IdleStateEvent) { IdleStateEvent event = (IdleStateEvent) obj; if (IdleState.READER_IDLE.equals(event.state())) { //如果读通道处于空闲状态,说明没有接收到心跳命令 System.out.println("已经5秒没有接收到客户端的信息了"); if (idle_count > 2) { System.out.println("关闭这个不活跃的channel"); ctx.channel().close(); } idle_count++; } } else { super.userEventTriggered(ctx, obj); } } /** * 业务逻辑处理 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("第"+count+"次"+",服务端接受的消息:"+msg); String message = (String) msg; if ("hb_request".equals(message)) { //如果是心跳命令,则发送给客户端;否则什么都不做 ctx.write("服务端成功收到心跳信息"); ctx.flush(); } count++; } /** * 异常处理 */ @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } }
服务端过滤器
这块增加了心跳的相关设置。
import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.socket.SocketChannel;import io.netty.handler.codec.string.StringDecoder;import io.netty.handler.codec.string.StringEncoder;import io.netty.handler.timeout.IdleStateHandler;import java.util.concurrent.TimeUnit;/** * * Title: HelloServerInitializer * Description: Netty 服务端过滤器 * Version:1.0.0 * @author pancm * @date 2017年10月8日 */public class NettyServerFilter extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline ph = ch.pipeline(); // 以("\n")为结尾分割的 解码器// ph.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); // 解码和编码,应和客户端一致 //入参说明: 读超时时间、写超时时间、所有类型的超时时间、时间格式 ph.addLast(new IdleStateHandler(5, 0, 0, TimeUnit.SECONDS)); ph.addLast("decoder", new StringDecoder()); ph.addLast("encoder", new StringEncoder()); ph.addLast("handler", new NettyServerHandler());// 服务端业务逻辑 } }
客户端
因为过滤器中没有使用DelimiterBasedFrameDecoder ,所以不必换行了。
import io.netty.bootstrap.Bootstrap;import io.netty.channel.Channel;import io.netty.channel.EventLoopGroup;import io.netty.channel.nio.NioEventLoopGroup;import io.netty.channel.socket.nio.NioSocketChannel;import java.io.IOException;/** * * Title: NettyClient* Description: Netty客户端 * Version:1.0.0 * @author pancm* @date 2017年10月8日 */public class NettyClient { public static String host = "127.0.0.1"; //ip地址 public static int port = 9876; //端口 /// 通过nio方式来接收连接和处理连接 private static EventLoopGroup group = new NioEventLoopGroup(); private static Bootstrap b = new Bootstrap(); private static Channel ch; /** * Netty创建全部都是实现自AbstractBootstrap。 * 客户端的是Bootstrap,服务端的则是 ServerBootstrap。 **/ public static void main(String[] args) throws InterruptedException, IOException { System.out.println("客户端成功启动..."); b.group(group); b.channel(NioSocketChannel.class); b.handler(new NettyClientFilter()); // 连接服务端 ch = b.connect(host, port).sync().channel(); star(); } public static void star() throws IOException{ String str="Hello Netty"; ch.writeAndFlush(str);// ch.writeAndFlush(str+ "\r\n"); System.out.println("客户端发送数据:"+str); } }
客户端业务逻辑处理
简单的在userEventTriggered 做了下相应的逻辑处理。
import io.netty.buffer.ByteBuf;import io.netty.buffer.Unpooled;import io.netty.channel.ChannelHandlerContext;import io.netty.channel.ChannelInboundHandlerAdapter;import io.netty.handler.timeout.IdleState;import io.netty.handler.timeout.IdleStateEvent;import io.netty.util.CharsetUtil;import java.util.Date;/** * * Title: NettyClientHandler* Description: 客户端业务逻辑实现* Version:1.0.0 * @author pancm* @date 2017年10月8日 */public class NettyClientHandler extends ChannelInboundHandlerAdapter { /** 客户端请求的心跳命令 */ private static final ByteBuf HEARTBEAT_SEQUENCE = Unpooled.unreleasableBuffer(Unpooled.copiedBuffer("hb_request", CharsetUtil.UTF_8)); /** 空闲次数 */ private int idle_count = 1; /** 发送次数 */ private int count = 1; /**循环次数 */ private int fcount = 1; /** * 建立连接时 */ @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { System.out.println("建立连接时:"+new Date()); ctx.fireChannelActive(); } /** * 关闭连接时 */ @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { System.out.println("关闭连接时:"+new Date()); } /** * 心跳请求处理 * 每4秒发送一次心跳请求; * */ @Override public void userEventTriggered(ChannelHandlerContext ctx, Object obj) throws Exception { System.out.println("循环请求的时间:"+new Date()+",次数"+fcount); if (obj instanceof IdleStateEvent) { IdleStateEvent event = (IdleStateEvent) obj; if (IdleState.WRITER_IDLE.equals(event.state())) { //如果写通道处于空闲状态,就发送心跳命令 if(idle_count <= 3){ //设置发送次数 idle_count++; ctx.channel().writeAndFlush(HEARTBEAT_SEQUENCE.duplicate()); }else{ System.out.println("不再发送心跳请求了!"); } fcount++; } } } /** * 业务逻辑处理 */ @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { System.out.println("第"+count+"次"+",客户端接受的消息:"+msg); count++; } }
客户端过滤器
几乎和服务端一致,除了心跳相关设置。
import io.netty.channel.ChannelInitializer;import io.netty.channel.ChannelPipeline;import io.netty.channel.socket.SocketChannel;import io.netty.handler.codec.string.StringDecoder;import io.netty.handler.codec.string.StringEncoder;import io.netty.handler.timeout.IdleStateHandler;import java.util.concurrent.TimeUnit;/** * * Title: NettyClientFilter* Description: Netty客户端 过滤器* Version:1.0.0 * @author pancm* @date 2017年10月8日 */public class NettyClientFilter extends ChannelInitializer<SocketChannel> { @Override protected void initChannel(SocketChannel ch) throws Exception { ChannelPipeline ph = ch.pipeline(); /* * 解码和编码,应和服务端一致 * */// ph.addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter())); //入参说明: 读超时时间、写超时时间、所有类型的超时时间、时间格式 //因为服务端设置的超时时间是5秒,所以设置4秒 ph.addLast( new IdleStateHandler(0, 4, 0, TimeUnit.SECONDS)); ph.addLast("decoder", new StringDecoder()); ph.addLast("encoder", new StringEncoder()); ph.addLast("handler", new NettyClientHandler()); //客户端的逻辑 }}
效果图:
服务端:
客户端
其它
本文只是个简单的心跳测试,所以在细节方面可能有所欠缺。 如有不妥,欢迎指出!
参考:http://blog.csdn.net/linuu/article/details/51404264
- Netty4 学习笔记之二:客户端与服务端心跳 demo
- Netty4 学习笔记之一:客户端与服务端通信 demo
- WebService学习笔记(二)服务端Demo
- 服务端与客户端握手规则,心跳逻辑
- 网络编程学习笔记(三)TCP协议及客户端与服务端交互Demo
- android学习笔记之客户端与服务端保持session登录状态
- Android学习笔记之Socket客户端与服务端通信( 一)
- Mina服务端客户端心跳机制
- NIO 客户端与服务端通信demo
- webService 服务端与客户端的Demo
- python笔记----客户端与服务端
- ROS学习笔记(九):客户端与服务端
- ROS学习笔记-2: 编写服务端与客户端
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Netty4.0学习笔记系列之二:Handler的执行顺序
- Netty4.0学习笔记系列之二:Handler的执行顺序
- leetcode 385. Mini Parser
- 链表中倒数第k个节点
- leetcode 92.Reverse Linked List II
- HTML 动画
- 一个确定初始聚类中心的更好方法
- Netty4 学习笔记之二:客户端与服务端心跳 demo
- Java多态性理解
- 23种设计模式介绍
- 关于集合的杂项知识点
- unity 3D C# 变量继承时如何重写?
- 知乎网站胡说八道,误人子弟!
- 使用HttpClient请求数据显示在ListView上
- Educational Codeforces Round 23 F. MEX Queries(线段树区间设值)
- 分布式系统基础-分布式事务