netty初识

netty是什么?

       Netty是一款基于NIO(Nonblocking I/O,非阻塞IO)开发的网络通信框架,可以快速轻松地开发高性能、高可靠性协议服务器和客户端之类的网络应用程序。它大大简化了网络编程流程,如TCP和UDP套接字服务器。

netty特性

       并发高

             对比于BIO(Blocking I/O,阻塞IO),他的并发性能得到了很大提高。

             如图:

                            
                                                                     阻塞IO的通信方式

 

 
                             
                                                                      非阻塞IO的通信方式
 
    nio和bio工作模式区别:
当一个连接建立之后,他有两个步骤要做,第一步是接收完客户端发过来的全部数据,第二步是服务端处理完请求业务之后返回response给客户端。NIO和BIO的区别主要是在第一步。
在BIO中,等待客户端发数据这个过程是阻塞的,这样就造成了一个线程只能处理一个请求的情况,而机器能支持的最大线程数是有限的,这就是为什么BIO不能支持高并发的原因。
而NIO中,当一个Socket建立好之后,Thread并不会阻塞去接受这个Socket,而是将这个请求交给Selector,Selector会不断的去遍历所有的Socket,一旦有一个Socket建立完成,他会通知Thread,然后Thread处理完数据再返回给客户端——这个过程是阻塞的,这样就能让一个Thread处理更多的请求了。
 

传输快

封装好


编写netty的"Hello World"
 
1.下载并为项目添加Netty框架
           Netty的包大家可以从Netty官网:http://netty.io/downloads.html 下载
                

2.创建Server 服务端

     Netty创建全部都是实现自AbstractBootstrap。客户端的是Bootstrap,服务端的则是ServerBootstrap。

  2.1 创建一个 HelloServer

/**
 * Author:   xinyuzhuang
 * Date:     2018/7/17 14:15
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.daojia;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;


/**
 *created by xinyuzhuang
 */
public class NettyServer1 {
    public static void main(String[] args) {
        //用于处理服务器端接收客户端连接
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
       //进行网络通信
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        //辅助工具类,用于服务器通道的一系列配置
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup,workerGroup) //绑定两个线程
                    .channel(NioServerSocketChannel.class) //制定nio的模式
                     .childHandler(new ChannelInitializer<SocketChannel>() {
                         @Override
                         protected void initChannel(SocketChannel socketChannel) throws Exception {
                             /*系统自带定长解决粘包问题
                             socketChannel.pipeline().addLast(new FixedLengthFrameDecoder(40));
                             系统自带字符串解码
                             socketChannel.pipeline().addLast(new StringDecoder());
                             socketChannel.pipeline().addLast(new InboundHandler());*/
                         }
                     }).option(ChannelOption.SO_BACKLOG,128) //设置tcp缓冲区
                    .option(ChannelOption.SO_SNDBUF, 32 * 1024) //设置发送数据缓冲大小
                    .option(ChannelOption.SO_RCVBUF, 32 * 1024) //设置接受数据缓冲大小
                     .childOption(ChannelOption.SO_KEEPALIVE, true); //保持连接
            ChannelFuture future = bootstrap.bind(8000).sync();
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }

    private static class InboundHandler extends ChannelInboundHandlerAdapter{
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf byteBuf = (ByteBuf) msg;
            byte[] data = new byte[byteBuf.readableBytes()];
            byteBuf.readBytes(data);
            String request = new String(data,"utf-8");
            System.out.println("server:"+request);
            //反馈给客户端
            ctx.writeAndFlush(Unpooled.copiedBuffer("sachjscfsjcssjcs".getBytes()));
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    }
}

        EventLoopGroup 是在4.x版本中提出来的一个新概念。用于channel的管理。服务端需要两个。和3.x版本一样,一个是boss线程一个是worker线程。 server主要 目的绑定客户端端口号。 

         FixedLengthFrameDecoder()和 StringDecoder()是系统自带的方法,InboundHandler()自己的Handler用于写自己的处理逻辑,在channelHandlerContent自带一个writeAndFlush方法,方法的作用是写入Buffer并刷入。

       注意:在3.x版本中此处有很大区别。在3.x版本中write()方法是自动flush的。在4.x版本的前面几个版本也是一样的。但是在4.0.9之后修改为WriteAndFlush。普通的write方法将不会发送消息。需要手动在write之后flush()一次。

       channelRead0 在这里的作用是类似于3.x版本的messageReceived()。可以当做是每一次收到消息是触发。

 

 2.2 创建一个 HelloClient

/**
 * Author:   xinyuzhuang
 * Date:     2018/7/17 15:06
 * History:
 * <author>          <time>          <version>          <desc>
 * 作者姓名           修改时间           版本号              描述
 */
package com.daijia.dmqclienrt;

import com.sun.org.apache.bcel.internal.classfile.ConstantValue;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.util.ReferenceCountUtil;

/**
 *created by xinyuzhuang
 */
public class NettyClient1 {
    public static void main(String[] args) {
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();
        Bootstrap bootstrap = new Bootstrap();
        try {
            bootstrap.group(workerGroup)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                            socketChannel.pipeline().addLast(new ClientInboundHandler());
                        }
                    });
            ChannelFuture future = bootstrap.connect("127.0.0.1", 8000).sync();
            future.channel().writeAndFlush(Unpooled.copiedBuffer("让我机动车道".getBytes()));
            future.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
        }
    }

    private static class ClientInboundHandler extends ChannelInboundHandlerAdapter{
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf buf = (ByteBuf) msg;
            byte[] data = new byte[buf.readableBytes()];
            buf.readBytes(data);
            System.out.println("Client:"+new String(data,"utf-8"));

            //ctx.writeAndFlush(Unpooled.copiedBuffer("基础撒即可产生".getBytes()));
            ReferenceCountUtil.release(msg);

        }

        @Override
        public void channelActive(ChannelHandlerContext ctx) throws Exception {
            byte[] req = "金粉世家开放式空间".getBytes();
            ByteBuf message = null;
            for (int i = 0; i < 100; i++) {
                message = Unpooled.buffer(req.length);
                message.writeBytes(req);
                ctx.writeAndFlush(message);
            }
            }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
            cause.printStackTrace();
            ctx.close();
        }
    }
}

 

2.3 HandlerInitializer详解

         Handler在Netty中是一个比较重要的概念。有着相当重要的作用。相比于Netty的底层。我们接触更多的应该是他的Handler。在这里我将它剥离出来单独解释。

        ChannelInitializer<SocketChannel>继承ChannelInboundHandlerAdapter类,需要我们实现一个initChannel()方法。我们定义的handler就是写在这里面。

    在最开始的地方定义了一个FixedLengthFrameDecoder类。按直接翻译就是基于字符定长的帧解码器。再一次感觉框架的作者的命名,好直接好简单。详细的内容我们在后面的文章中在为大家详细的解释。

    下面的则是StringDecoder 和StringEncoder。字符串解码器和字符串编码器。

    最后面则是我们自己的逻辑。服务/客户端逻辑是在消息解码之后处理的。然后服务/客户端返回相关消息则是需要对消息进行相对应的编码。最终才是以二进制数据流的形势发送给服务/客户端的。

注:

    ChannelInBoundHandler负责数据进入并在ChannelPipeline中按照从上至下的顺序查找调用相应的InBoundHandler。

    ChannelOutBoundHandler负责数据出去并在ChannelPipeline中按照从下至上的顺序查找调用相应的OutBoundHandler。

Netty其实本质上就是Reactor模式的实现,Selector作为多路复用器,EventLoop作为转发器,Pipeline作为事件处理器。但是和一般的Reactor不同的是,Netty使用串行化实现,并在Pipeline中使用了责任链模式。

posted @ 2018-07-26 17:22  ilzs  Views(153)  Comments(0)    收藏  举报