Netty 4.0 实现心跳检测和断线重连

一 实现心跳检测 
原理:当服务端每隔一段时间就会向客户端发送心跳包,客户端收到心跳包后一样也会回一个心跳包给服务端 
通常状况下,客户端与服务端在指定时间内没有任何读写请求,就会认为链接是idle(空闲的)的。此时,客户端须要向服务端发送心跳消息,来维持服务端与客户端的连接。那么怎么判断客户端在指定时间里没有任何读写请求呢?netty中为咱们提供一个特别好用的IdleStateHandler来干这个苦差事! 

在服务端工做线程中添加: 
java

Java代码  收藏代码android

  1. arg0.pipeline().addLast("ping"new IdleStateHandler(251510,TimeUnit.SECONDS));  bootstrap



这个处理器,它的做用就是用来检测客户端的读取超时的,该类的第一个参数是指定读操做空闲秒数,第二个参数是指定写操做的空闲秒数,第三个参数是指定读写空闲秒数,当有操做操做超出指定空闲秒数时,便会触发UserEventTriggered事件。因此咱们只须要在本身的handler中截获该事件,而后发起相应的操做便可(好比说发起心跳操做)。如下是咱们自定义的handler中的代码: 
服务器

Java代码  收藏代码socket

  1. /** ide

  2.      * 一段时间未进行读写操做 回调 函数

  3.      */  oop

  4.     @Override  spa

  5.     public void userEventTriggered(ChannelHandlerContext ctx, Object evt)  .net

  6.             throws Exception {  

  7.         // TODO Auto-generated method stub  

  8.         super.userEventTriggered(ctx, evt);  

  9.   

  10.         if (evt instanceof IdleStateEvent) {  

  11.   

  12.             IdleStateEvent event = (IdleStateEvent) evt;  

  13.               

  14.             if (event.state().equals(IdleState.READER_IDLE)) {  

  15.                 //未进行读操做  

  16.                 System.out.println("READER_IDLE");  

  17.                 // 超时关闭channel  

  18.                  ctx.close();  

  19.   

  20.             } else if (event.state().equals(IdleState.WRITER_IDLE)) {  

  21.                   

  22.   

  23.             } else if (event.state().equals(IdleState.ALL_IDLE)) {  

  24.                 //未进行读写  

  25.                 System.out.println("ALL_IDLE");  

  26.                 // 发送心跳消息  

  27.                 MsgHandleService.getInstance().sendMsgUtil.sendHeartMessage(ctx);  

  28.                   

  29.             }  

  30.   

  31.         }  

  32.     }  



也就是说 服务端在10s内未进行读写操做,就会向客户端发送心跳包,客户端收到心跳包后当即回复心跳包给服务端,此时服务端就进行了读操做,也就不会触发IdleState.READER_IDLE(未读操做状态),若客户端异常掉线了,并不能响应服务端发来的心跳包,在25s后就会触发IdleState.READER_IDLE(未读操做状态),此时服务器就会将通道关闭 

客户端代码略 


二 客户端实现断线重连 
原理当客户端链接服务器时 

Java代码  收藏代码

  1. bootstrap.connect(new InetSocketAddress(  

  2.                     serverIP, port));  


会返回一个ChannelFuture的对象,咱们对这个对象进行监听 
代码以下: 

Java代码  收藏代码

  1. import android.os.Handler;  

  2. import android.os.HandlerThread;  

  3. import android.os.Message;  

  4. import android.util.Log;  

  5.   

  6. import com.ld.qmwj.Config;  

  7. import com.ld.qmwj.MyApplication;  

  8.   

  9. import java.net.InetSocketAddress;  

  10. import java.nio.charset.Charset;  

  11. import java.util.concurrent.TimeUnit;  

  12.   

  13. import io.netty.bootstrap.Bootstrap;  

  14. import io.netty.buffer.ByteBuf;  

  15. import io.netty.buffer.Unpooled;  

  16. import io.netty.channel.ChannelFuture;  

  17. import io.netty.channel.ChannelFutureListener;  

  18. import io.netty.channel.ChannelInitializer;  

  19. import io.netty.channel.ChannelOption;  

  20. import io.netty.channel.nio.NioEventLoopGroup;  

  21. import io.netty.channel.socket.SocketChannel;  

  22. import io.netty.channel.socket.nio.NioSocketChannel;  

  23. import io.netty.handler.codec.DelimiterBasedFrameDecoder;  

  24. import io.netty.handler.codec.string.StringDecoder;  

  25. import io.netty.handler.codec.string.StringEncoder;  

  26.   

  27. /** 

  28.  * Created by zsg on 2015/11/21. 

  29.  */  

  30. public class MyClient implements Config {  

  31.     private static Bootstrap bootstrap;  

  32.     private static ChannelFutureListener channelFutureListener = null;  

  33.   

  34.     public MyClient() {  

  35.   

  36.     }  

  37.   

  38.   

  39.     // 初始化客户端  

  40.     public static void initClient() {  

  41.   

  42.         NioEventLoopGroup group = new NioEventLoopGroup();  

  43.   

  44.         // Client服务启动器 3.x的ClientBootstrap  

  45.         // 改成Bootstrap,且构造函数变化很大,这里用无参构造。  

  46.         bootstrap = new Bootstrap();  

  47.         // 指定EventLoopGroup  

  48.         bootstrap.group(group);  

  49.         // 指定channel类型  

  50.         bootstrap.channel(NioSocketChannel.class);  

  51.         // 指定Handler  

  52.         bootstrap.handler(new ChannelInitializer<SocketChannel>() {  

  53.             @Override  

  54.             protected void initChannel(SocketChannel ch) throws Exception {  

  55.                 // 建立分隔符缓冲对象  

  56.                 ByteBuf delimiter = Unpooled.copiedBuffer("#"  

  57.                         .getBytes());  

  58.                 // 当达到最大长度仍没找到分隔符 就抛出异常  

  59.                 ch.pipeline().addLast(  

  60.                         new DelimiterBasedFrameDecoder(10000truefalse, delimiter));  

  61.                 // 将消息转化成字符串对象 下面的到的消息就不用转化了  

  62.                 //解码  

  63.                 ch.pipeline().addLast(new StringEncoder(Charset.forName("UTF-8")));  

  64.                 ch.pipeline().addLast(new StringDecoder(Charset.forName("GBK")));  

  65.                 ch.pipeline().addLast(new MyClientHandler());  

  66.             }  

  67.         });  

  68.         //设置TCP协议的属性  

  69.         bootstrap.option(ChannelOption.SO_KEEPALIVE, true);  

  70.         bootstrap.option(ChannelOption.TCP_NODELAY, true);  

  71.         bootstrap.option(ChannelOption.SO_TIMEOUT, 5000);  

  72.   

  73.         channelFutureListener = new ChannelFutureListener() {  

  74.             public void operationComplete(ChannelFuture f) throws Exception {  

  75.                 //  Log.d(Config.TAG, "isDone:" + f.isDone() + "     isSuccess:" + f.isSuccess() +  

  76.                 //          "     cause" + f.cause() + "        isCancelled" + f.isCancelled());  

  77.   

  78.                 if (f.isSuccess()) {  

  79.                     Log.d(Config.TAG, "从新链接服务器成功");  

  80.   

  81.                 } else {  

  82.                     Log.d(Config.TAG, "从新链接服务器失败");  

  83.                     //  3秒后从新链接  

  84.                     f.channel().eventLoop().schedule(new Runnable() {  

  85.                         @Override  

  86.                         public void run() {  

  87.                             doConnect();  

  88.                         }  

  89.                     }, 3, TimeUnit.SECONDS);  

  90.                 }  

  91.             }  

  92.         };  

  93.   

  94.     }  

  95.   

  96.     //  链接到服务端  

  97.     public static void doConnect() {  

  98.         Log.d(TAG, "doConnect");  

  99.         ChannelFuture future = null;  

  100.         try {  

  101.             future = bootstrap.connect(new InetSocketAddress(  

  102.                     serverIP, port));  

  103.             future.addListener(channelFutureListener);  

  104.   

  105.         } catch (Exception e) {  

  106.             e.printStackTrace();  

  107.             //future.addListener(channelFutureListener);  

  108.             Log.d(TAG, "关闭链接");  

  109.         }  

  110.   

  111.     }  

  112.   

  113. }  



监听到链接服务器失败时,会在3秒后从新链接(执行doConnect方法) 

这还不够,当客户端掉线时要进行从新链接 
在咱们本身定义逻辑处理的Handler中 

Java代码  收藏代码

  1. @Override  

  2.     public void channelInactive(ChannelHandlerContext ctx) throws Exception {  

  3.         Log.d(Config.TAG, "与服务器断开链接服务器");  

  4.         super.channelInactive(ctx);  

  5.         MsgHandle.getInstance().channel = null;  

  6.   

  7.         //从新链接服务器  

  8.         ctx.channel().eventLoop().schedule(new Runnable() {  

  9.             @Override  

  10.             public void run() {  

  11.                 MyClient.doConnect();  

  12.             }  

  13.         }, 2, TimeUnit.SECONDS);  

  14.         ctx.close();  

  15.     }  

相关文章
相关标签/搜索