• 关于Netty的入门使用


    Netty介绍:

    Netty是一个提供异步事件驱动的网络应用框架,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

    换句话说,Netty是一个NIO框架,使用它可以简单快速地开发网络应用程序,比如客户端和服务端的协议。Netty大大简化了网络程序的开发过程比如TCP和UDP的 Socket的开发。

    “快速和简单”并不意味着应用程序会有难维护和性能低的问题,Netty是一个精心设计的框架,它从许多协议的实现中吸收了很多的经验比如FTP、SMTP、HTTP、许多二进制和基于文本的传统协议,Netty在不降低开发效率、性能、稳定性、灵活性情况下,成功地找到了解决方案。

    有一些用户可能已经发现其他的一些网络框架也声称自己有同样的优势,所以你可能会问是Netty和它们的不同之处。答案就是Netty的哲学设计理念。Netty从第一天开始就为用户提供了用户体验最好的API以及实现设计。正是因为Netty的设计理念,才让我们得以轻松地阅读本指南并使用Netty。

    接下来,我们看下Client端的代码实现:

     1 package ruizhan.hjf.netty;
     2 
     3 import io.netty.bootstrap.Bootstrap;
     4 import io.netty.buffer.Unpooled;
     5 import io.netty.channel.ChannelFuture;
     6 import io.netty.channel.ChannelInitializer;
     7 import io.netty.channel.ChannelOption;
     8 import io.netty.channel.EventLoopGroup;
     9 import io.netty.channel.nio.NioEventLoopGroup;
    10 import io.netty.channel.socket.SocketChannel;
    11 import io.netty.channel.socket.nio.NioSocketChannel;
    12 /**
    13  * Netty客户端的程序
    14  * @author huangjianfei
    15  */
    16 public class Client {
    17     /*IP地址*/
    18     static final String HOST = System.getProperty("host", "127.0.0.1");
    19     /*端口号*/
    20     static final int PORT1 = Integer.parseInt(System.getProperty("port", "8765"));
    21     
    22     static final int PORT2 = Integer.parseInt(System.getProperty("port", "8764"));
    23     
    24     public static void main(String[] args) throws Exception {
    25         EventLoopGroup workgroup = new NioEventLoopGroup();
    26         Bootstrap b = new Bootstrap();//客户端
    27         b.group(workgroup)
    28         .channel(NioSocketChannel.class)//客户端 -->NioSocketChannel
    29         .option(ChannelOption.SO_KEEPALIVE, true)
    30         .handler(new ChannelInitializer<SocketChannel>() {//handler
    31             @Override
    32             protected void initChannel(SocketChannel sc) throws Exception {
    33                 sc.pipeline().addLast(new ClientHandler());  
    34             }
    35         });  
    36         //创建异步连接 可添加多个端口
    37         ChannelFuture cf1 = b.connect(HOST, PORT1).sync();
    38         ChannelFuture cf2 = b.connect(HOST, PORT2).sync();   
    39         
    40         //buf
    41         //client向server端发送数据  Buffer形式
    42         cf1.channel().writeAndFlush(Unpooled.copiedBuffer("hello netty".getBytes()));
    43         cf2.channel().writeAndFlush(Unpooled.copiedBuffer("hello world".getBytes()));
    44         
    45         
    46         cf1.channel().closeFuture().sync();
    47         cf2.channel().closeFuture().sync();
    48         
    49         workgroup.shutdownGracefully();
    50     }
    51 }

    Servler端代码实现:

     1 package ruizhan.hjf.netty;
     2 
     3 import io.netty.bootstrap.ServerBootstrap;
     4 import io.netty.buffer.Unpooled;
     5 import io.netty.channel.ChannelFuture;
     6 import io.netty.channel.ChannelInitializer;
     7 import io.netty.channel.ChannelOption;
     8 import io.netty.channel.EventLoopGroup;
     9 import io.netty.channel.nio.NioEventLoopGroup;
    10 import io.netty.channel.socket.SocketChannel;
    11 import io.netty.channel.socket.nio.NioServerSocketChannel;
    12 /**
    13  * Netty实现的服务端程序
    14  * @author huangjianfei
    15  */
    16 public class Server
    17 {
    18     /*端口号*/
    19     static final int PORT1 = Integer.parseInt(System.getProperty("port", "8765"));
    20     
    21     static final int PORT2 = Integer.parseInt(System.getProperty("port", "8764"));
    22     
    23     public static void main(String[] args)
    24     {
    25         EventLoopGroup bossGroup = null;
    26         EventLoopGroup workerGroup = null;
    27         ServerBootstrap b = null;
    28         try{
    29             //1:第一个线程组是用于接收Client连接的
    30             bossGroup = new NioEventLoopGroup(); //(1)
    31             //2:第二个线程组是用于实际的业务处理操作的
    32             workerGroup = new NioEventLoopGroup();
    33             //3:创建一个启动NIO服务的辅助启动类ServerBootstrap 就是对我们的Server进行一系列的配置
    34             b = new ServerBootstrap();//(2)
    35             //4:绑定两个线程组
    36             b.group(bossGroup, workerGroup)
    37             //5:需要指定使用NioServerSocketChannel这种类型的通道
    38             .channel(NioServerSocketChannel.class)//(3) 服务端 -->NioServerSocketChannel
    39             //6:一定要使用childHandler 去绑定具体的事件处理器
    40             .childHandler(new ChannelInitializer<SocketChannel>() //(4)   childHandler
    41             {
    42                 @Override
    43                 protected void initChannel(SocketChannel sc) throws Exception
    44                 {
    45                     //7:将自定义的serverHandler加入到管道中去(多个)
    46                     sc.pipeline().addLast(new ServerHandler());//handler中实现真正的业务逻辑
    47 //                    sc.pipeline().addLast(new ServerHandler());
    48 //                    sc.pipeline().addLast(new ServerHandler());
    49                 }
    50             })
    51             /**
    52              * 服务器端TCP内核模块维护两个队列,我们称之为A,B吧
    53              * 客户端向服务端connect的时候,会发送带有SYN标志的包(第一次握手)
    54              * 服务端收到客户端发来的SYN时,向客户端发送SYN ACK确认(第二次握手)
    55              * 此时TCP内核模块把客户端连接加入到A队列中,最后服务端收到客户端发来的ACK时(第三次握手)
    56              * TCP内核模块把客户端连接从A队列移到B队列,连接成功,应用程序的accept会返回
    57              * 也就是说accept从B队列中取出完成三次握手的连接
    58              * A队列和B队列的长度之和是backLog,当A,B队列的长度之和大于backLog时,新连接将会被TCP内核拒绝
    59              * 所以,如果backLog过小,可能会出现accept速度跟不上,A,B队列满了,导致新的客户端无法连接,
    60              * 要注意的是,backLog对程序支持的连接数并无影响,backLog影响的只是还没有被accept取出的连接
    61              */
    62             //8:设置TCP连接的缓冲区
    63             .option(ChannelOption.SO_BACKLOG, 128)//(5)
    64 //            .option(ChannelOption.SO_SNDBUF, 32*1024) //设置发送缓冲大小
    65 //            .option(ChannelOption.SO_RCVBUF, 32*1024) //设置接收缓冲大小
    66             //9:保持连接
    67             .childOption(ChannelOption.SO_KEEPALIVE, true);//(6)
    68             //10:绑定指定的端口 进行监听
    69             //此处端口号先写死  也可以绑定多个端口  
    70             ChannelFuture cf2= b.bind(PORT1).sync(); // (7)  
    71             
    72             ChannelFuture cf3= b.bind(PORT2).sync(); // (7)   绑定多个端口 
    73 
    74             //Thread.sleep(10000);
    75             cf2.channel().closeFuture().sync(); //异步等待关闭 
    76             cf3.channel().closeFuture().sync(); //异步等待关闭
    77             
    78         }catch(Exception e){
    79             e.printStackTrace();
    80         }finally{
    81             workerGroup.shutdownGracefully();
    82             bossGroup.shutdownGracefully();
    83         }
    84     }
    85 }

    接下来,就是真正去实现数据传输的业务逻辑层代码的实现,在这里也就是ClientHanlder和ServlerHandler:

     1 package ruizhan.hjf.netty;
     2 
     3 import io.netty.buffer.ByteBuf;
     4 import io.netty.buffer.Unpooled;
     5 import io.netty.channel.ChannelHandlerAdapter;
     6 import io.netty.channel.ChannelHandlerContext;
     7 import io.netty.util.ReferenceCountUtil;
     8 
     9 /**
    10  * 客户端业务处理类
    11  * (编写主要的业务逻辑)
    12  * @author huangjianfei
    13  */
    14 public class ClientHandler extends ChannelHandlerAdapter
    15 {
    16     // ByteBuf是一个引用计数对象,这个对象必须显示地调用release()方法来释放。
    17     // 请记住处理器的职责是释放所有传递到处理器的引用计数对象。
    18     @Override
    19     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
    20     {
    21         try{
    22             //do something
    23             //接收服务端发来的数据 ByteBuf
    24             ByteBuf  buf = (ByteBuf)msg;
    25             //创建一个和buf一样长度的字节空数组
    26             byte[] data = new byte[buf.readableBytes()];
    27             //将buf中的数据读取到data数组中
    28             buf.readBytes(data);
    29             //将data数组惊醒包装 以String格式输出
    30             String response = new String(data,"utf-8");
    31             System.out.println("client :"+response);
    32             
    33             //以上代码是接收服务端发来的反馈数据//
    34             
    35             ctx.close();
    36         }finally{
    37             // Discard the received data silently.
    38             ReferenceCountUtil.release(msg);
    39         }
    40     }
    41 
    42     @Override
    43     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
    44     {
    45         // Close the connection when an exception is raised.
    46         cause.printStackTrace();
    47         ctx.close();
    48     }
    49 }
     1 package ruizhan.hjf.netty;
     2 
     3 import io.netty.buffer.ByteBuf;
     4 import io.netty.buffer.Unpooled;
     5 import io.netty.channel.ChannelFutureListener;
     6 import io.netty.channel.ChannelHandlerAdapter;
     7 import io.netty.channel.ChannelHandlerContext;
     8 import io.netty.util.ReferenceCountUtil;
     9 
    10 /**
    11  * 服务端业务处理类
    12  * (编写主要的业务逻辑)
    13  * @author huangjianfei
    14  */
    15 public class ServerHandler extends ChannelHandlerAdapter
    16 {
    17 
    18     /**
    19      * 每当从客户端收到新的数据时,这个方法会在收到消息时被调用
    20      * ByteBuf是一个引用计数对象,这个对象必须显示地调用release()方法来释放。
    21      * 请记住处理器的职责是释放所有传递到处理器的引用计数对象。
    22      */
    23     @Override
    24     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception
    25     {
    26         try{
    27             //do something
    28             //接收客户端发送的数据 ByteBuf
    29             ByteBuf buf = (ByteBuf)msg;
    30             //创建一个和buf长度一样的空字节数组
    31             byte[] data = new byte[buf.readableBytes()];
    32             //将buf中的数据读取到data数组中
    33             buf.readBytes(data);
    34             //将data数据包装成string输出
    35             String request = new String(data,"utf-8");
    36             System.out.println("server :"+request);
    37     
    38             //以上代码是接收客户端信息//
    39             
    40             //server端向client发送反馈数据
    41             //如果是绑定了多个端口 那么都会进行发送
    42             ctx.writeAndFlush(Unpooled.copiedBuffer("888".getBytes()))
    43             .addListener(ChannelFutureListener.CLOSE);//添加监听 当服务端向客户端发送完数据后,关闭connect连接
    44             /**
    45              * ChannelFutureListener,当一个写请求完成时通知并且关闭Channel
    46              * 加上监听 意味着服务端回送数据到客户端时 连接关闭(短连接)
    47              * 不加监听 意味着客户端与服务端一直保持连接状态(长连接)
    48              */
    49             
    50             
    51             ctx.close();
    52         }finally{
    53             // Discard the received data silently.
    54             ReferenceCountUtil.release(msg);
    55         }
    56     }
    57 
    58     /**
    59      * exceptionCaught()事件处理方法是当出现Throwable对象才会被调用
    60      * 即当Netty由于IO错误或者处理器在处理事件时抛出的异常时
    61      */
    62     @Override
    63     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception
    64     {
    65         // Close the connection when an exception is raised.
    66         cause.printStackTrace();
    67         ctx.close();
    68     }
    69  
    70 }

    以上是Netty的基础入门实现,详见并发编程网,http://ifeve.com/netty5-user-guide/

  • 相关阅读:
    1583: [Usaco2009 Mar]Moon Mooing 哞哞叫
    1734: [Usaco2005 feb]Aggressive cows 愤怒的牛
    1596: [Usaco2008 Jan]电话网络
    1589: [Usaco2008 Dec]Trick or Treat on the Farm 采集糖果
    机器学习笔记(2)单变量线性回归
    P4编程环境搭建遇到的问题与解决方法
    P4编程环境搭建
    机器学习笔记(1)监督学习和无监督学习
    阅读 用P4对数据平面进行编程
    Mininet实验 MAC地址学习分析
  • 原文地址:https://www.cnblogs.com/little-fly/p/8683197.html
Copyright © 2020-2023  润新知