• 架构师养成记--22.客户端与服务器端保持连接的解决方案,netty的ReadTimeoutHandler


    概述

    保持客户端与服务器端连接的方案常用的有3种

    1.长连接,也就是客户端与服务器端一直保持连接,适用于客户端比较少的情况。

    2.定时段连接,比如在某一天的凌晨建立连接,适用于对实时性要求不高的情况。

    3.设置连接超时,比如超过1分钟没有传输数据就断开连接,等下次需要的时候再建立连接,这种方案比较常用。

    netty的ReadTimeOut实现方案3

    服务端 

    大部分代码都保持不变,有变化的代码在第30行,设置服务端的超时时间

     1 import io.netty.bootstrap.ServerBootstrap;
     2 import io.netty.channel.ChannelFuture;
     3 import io.netty.channel.ChannelInitializer;
     4 import io.netty.channel.ChannelOption;
     5 import io.netty.channel.EventLoopGroup;
     6 import io.netty.channel.nio.NioEventLoopGroup;
     7 import io.netty.channel.socket.SocketChannel;
     8 import io.netty.channel.socket.nio.NioServerSocketChannel;
     9 import io.netty.handler.logging.LogLevel;
    10 import io.netty.handler.logging.LoggingHandler;
    11 import io.netty.handler.timeout.ReadTimeoutHandler;
    12 
    13 public class Server {
    14 
    15     public static void main(String[] args) throws Exception{
    16         
    17         EventLoopGroup pGroup = new NioEventLoopGroup();
    18         EventLoopGroup cGroup = new NioEventLoopGroup();
    19         
    20         ServerBootstrap b = new ServerBootstrap();
    21         b.group(pGroup, cGroup)
    22          .channel(NioServerSocketChannel.class)
    23          .option(ChannelOption.SO_BACKLOG, 1024)
    24          //设置日志
    25          .handler(new LoggingHandler(LogLevel.INFO))
    26          .childHandler(new ChannelInitializer<SocketChannel>() {
    27             protected void initChannel(SocketChannel sc) throws Exception {
    28                 sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
    29                 sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
    30                 sc.pipeline().addLast(new ReadTimeoutHandler(5)); 
    31                 sc.pipeline().addLast(new ServerHandler());
    32             }
    33         });
    34         
    35         ChannelFuture cf = b.bind(8765).sync();
    36         
    37         cf.channel().closeFuture().sync();
    38         pGroup.shutdownGracefully();
    39         cGroup.shutdownGracefully();
    40         
    41     }
    42 }

    ServerHandler代码也没有什么变化

     1 import io.netty.channel.ChannelHandlerAdapter;
     2 import io.netty.channel.ChannelHandlerContext;
     3 
     4 public class ServerHandler extends ChannelHandlerAdapter{
     5 
     6     @Override
     7     public void channelActive(ChannelHandlerContext ctx) throws Exception {
     8 
     9     }
    10 
    11     @Override
    12     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    13         Request request = (Request)msg;
    14         System.out.println("Server : " + request.getId() + ", " + request.getName() + ", " + request.getRequestMessage());
    15         Response response = new Response();
    16         response.setId(request.getId());
    17         response.setName("response" + request.getId());
    18         response.setResponseMessage("响应内容" + request.getId());
    19         ctx.writeAndFlush(response);//.addListener(ChannelFutureListener.CLOSE);
    20     }
    21 
    22     @Override
    23     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    24         
    25     }
    26 
    27     @Override
    28     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    29         ctx.close();
    30     }
    31 
    32     
    33     
    34 }

    客户端

    客户端的代码也设置了超时时间(实际上只要服务器端设置也就可以了,有人说客户端不设置会出问题,现在还没有发现什么问题)。主要看getChannelFuture这个方法,this.cf == null是第一次连接的时候用到的,!this.cf.channel().isActive() 是连接超时后重新发起连接用到的。再看main方法,可以发现for(int i = 1; i <= 3; i++ ) 这个循环中,每个循环停顿4秒,也就是每隔4秒发送一次请求,而服务器端的超时时间设置为5秒,那么在这个for循环期间连接是不会断开的,等for循环结束 cf.channel().closeFuture().sync(); 断开连接this.cf.channel().isActive()  变为否,在new Thread()中再次发送请求,getChannelFuture会重新建立连接。

      1 import io.netty.bootstrap.Bootstrap;
      2 import io.netty.channel.ChannelFuture;
      3 import io.netty.channel.ChannelInitializer;
      4 import io.netty.channel.EventLoopGroup;
      5 import io.netty.channel.nio.NioEventLoopGroup;
      6 import io.netty.channel.socket.SocketChannel;
      7 import io.netty.channel.socket.nio.NioSocketChannel;
      8 import io.netty.handler.logging.LogLevel;
      9 import io.netty.handler.logging.LoggingHandler;
     10 import io.netty.handler.timeout.ReadTimeoutHandler;
     11 
     12 import java.util.concurrent.TimeUnit;
     13 
     14 
     15 /**
     16  * Best Do It
     17  */
     18 public class Client {
     19     
     20     private static class SingletonHolder {
     21         static final Client instance = new Client();
     22     }
     23     
     24     public static Client getInstance(){
     25         return SingletonHolder.instance;
     26     }
     27     
     28     private EventLoopGroup group;
     29     private Bootstrap b;
     30     private ChannelFuture cf ;
     31     
     32     private Client(){
     33             group = new NioEventLoopGroup();
     34             b = new Bootstrap();
     35             b.group(group)
     36              .channel(NioSocketChannel.class)
     37              .handler(new LoggingHandler(LogLevel.INFO))
     38              .handler(new ChannelInitializer<SocketChannel>() {
     39                     @Override
     40                     protected void initChannel(SocketChannel sc) throws Exception {
     41                         sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingDecoder());
     42                         sc.pipeline().addLast(MarshallingCodeCFactory.buildMarshallingEncoder());
     43                         //超时handler(当服务器端与客户端在指定时间以上没有任何进行通信,则会关闭响应的通道,主要为减小服务端资源占用)
     44                         sc.pipeline().addLast(new ReadTimeoutHandler(5)); 
     45                         sc.pipeline().addLast(new ClientHandler());
     46                     }
     47             });
     48     }
     49     
     50     public void connect(){
     51         try {
     52             this.cf = b.connect("127.0.0.1", 8765).sync();
     53             System.out.println("远程服务器已经连接, 可以进行数据交换..");                
     54         } catch (Exception e) {
     55             e.printStackTrace();
     56         }
     57     }
     58     
     59     public ChannelFuture getChannelFuture(){
     60         
     61         if(this.cf == null){
     62             this.connect();
     63         }
     64         if(!this.cf.channel().isActive()){
     65             this.connect();
     66         }
     67         
     68         return this.cf;
     69     }
     70     
     71     public static void main(String[] args) throws Exception{
     72         final Client c = Client.getInstance();
     73         //c.connect();
     74         
     75         ChannelFuture cf = c.getChannelFuture();
     76         for(int i = 1; i <= 3; i++ ){
     77             Request request = new Request();
     78             request.setId("" + i);
     79             request.setName("pro" + i);
     80             request.setRequestMessage("数据信息" + i);
     81             cf.channel().writeAndFlush(request);
     82             TimeUnit.SECONDS.sleep(4);
     83         }
     84 
     85         cf.channel().closeFuture().sync();
     86         
     87         
     88         new Thread(new Runnable() {
     89             @Override
     90             public void run() {
     91                 try {
     92                     System.out.println("进入子线程...");
     93                     ChannelFuture cf = c.getChannelFuture();
     94                     System.out.println(cf.channel().isActive());
     95                     System.out.println(cf.channel().isOpen());
     96                     
     97                     //再次发送数据
     98                     Request request = new Request();
     99                     request.setId("" + 4);
    100                     request.setName("pro" + 4);
    101                     request.setRequestMessage("数据信息" + 4);
    102                     cf.channel().writeAndFlush(request);                    
    103                     cf.channel().closeFuture().sync();
    104                     System.out.println("子线程结束.");
    105                 } catch (InterruptedException e) {
    106                     e.printStackTrace();
    107                 }
    108             }
    109         }).start();
    110         
    111         System.out.println("断开连接,主线程结束..");
    112         
    113     }
    114     
    115     
    116     
    117 }

    clientHandler没有什么变化

     1 import io.netty.channel.ChannelHandlerAdapter;
     2 import io.netty.channel.ChannelHandlerContext;
     3 import io.netty.util.ReferenceCountUtil;
     4 
     5 public class ClientHandler extends ChannelHandlerAdapter{
     6     
     7     @Override
     8     public void channelActive(ChannelHandlerContext ctx) throws Exception {
     9 
    10     }
    11 
    12     @Override
    13     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    14         try {
    15             Response resp = (Response)msg;
    16             System.out.println("Client : " + resp.getId() + ", " + resp.getName() + ", " + resp.getResponseMessage());            
    17         } finally {
    18             ReferenceCountUtil.release(msg);
    19         }
    20     }
    21 
    22     @Override
    23     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
    24         
    25     }
    26 
    27     @Override
    28     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    29         ctx.close();
    30     }
    31     
    32 }

    工厂类不变

     1 import io.netty.handler.codec.marshalling.DefaultMarshallerProvider;
     2 import io.netty.handler.codec.marshalling.DefaultUnmarshallerProvider;
     3 import io.netty.handler.codec.marshalling.MarshallerProvider;
     4 import io.netty.handler.codec.marshalling.MarshallingDecoder;
     5 import io.netty.handler.codec.marshalling.MarshallingEncoder;
     6 import io.netty.handler.codec.marshalling.UnmarshallerProvider;
     7 
     8 import org.jboss.marshalling.MarshallerFactory;
     9 import org.jboss.marshalling.Marshalling;
    10 import org.jboss.marshalling.MarshallingConfiguration;
    11 
    12 /**
    13  * Marshalling工厂
    14  * @author(alienware)
    15  * @since 2014-12-16
    16  */
    17 public final class MarshallingCodeCFactory {
    18 
    19     /**
    20      * 创建Jboss Marshalling解码器MarshallingDecoder
    21      * @return MarshallingDecoder
    22      */
    23     public static MarshallingDecoder buildMarshallingDecoder() {
    24         //首先通过Marshalling工具类的精通方法获取Marshalling实例对象 参数serial标识创建的是java序列化工厂对象。
    25         final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
    26         //创建了MarshallingConfiguration对象,配置了版本号为5 
    27         final MarshallingConfiguration configuration = new MarshallingConfiguration();
    28         configuration.setVersion(5);
    29         //根据marshallerFactory和configuration创建provider
    30         UnmarshallerProvider provider = new DefaultUnmarshallerProvider(marshallerFactory, configuration);
    31         //构建Netty的MarshallingDecoder对象,俩个参数分别为provider和单个消息序列化后的最大长度
    32         MarshallingDecoder decoder = new MarshallingDecoder(provider, 1024);
    33         return decoder;
    34     }
    35 
    36     /**
    37      * 创建Jboss Marshalling编码器MarshallingEncoder
    38      * @return MarshallingEncoder
    39      */
    40     public static MarshallingEncoder buildMarshallingEncoder() {
    41         final MarshallerFactory marshallerFactory = Marshalling.getProvidedMarshallerFactory("serial");
    42         final MarshallingConfiguration configuration = new MarshallingConfiguration();
    43         configuration.setVersion(5);
    44         MarshallerProvider provider = new DefaultMarshallerProvider(marshallerFactory, configuration);
    45         //构建Netty的MarshallingEncoder对象,MarshallingEncoder用于实现序列化接口的POJO对象序列化为二进制数组
    46         MarshallingEncoder encoder = new MarshallingEncoder(provider);
    47         return encoder;
    48     }
    49 }

    自定义的Request和Response对象没有什么变化,这里不再赘述

  • 相关阅读:
    Android使用SO库时要注意的一些问题
    android studio 生成引用arr
    android studio 改包名
    P2P通信原理与实现(C++)
    unity3d各种OpenFileDialog操作
    使用ffmpeg编码时,如何设置恒定码率,并控制好关键帧I帧间隔
    ffmpeg h264转h265
    照片人脸建模
    自动减面
    Unity3d 5.3.5使用sqlite3
  • 原文地址:https://www.cnblogs.com/sigm/p/6372520.html
Copyright © 2020-2023  润新知