Netty第一篇

netty 入门

Netty is a high performance IO toolkit for Java. Netty is open source, so you can use it freely, 
and even contribute to it if you want to. 

netty 是一个高效的java io 工具。 netty是开源的,你可以免费使用甚至是为其贡献源代码。 

netty 支持http , https,websocket,tcp,udp,vm pipe(这个是?)

主要的对象如下:

*Bootstrap

The Bootstrap classes in Netty take care of bootstrapping Netty. The bootstrapping process includes starting threads, opening sockets etc.

*EventLoopGroup

A Netty EventLoopGroup is a group of EventLoop’s . Multiple EventLoop’s can be grouped together. This way the EventLoop shares some resources like threads etc.

*EventLoop

A Netty EventLoop is a loop that keeps looking for new events, e.g. incoming data from network sockets (from SocketChannel) instances). When an event occurs, the event is passed on to the appropriate event handler, for instance a ChannelHandler.

*SocketChannel

A Netty SocketChannel represents a TCP connection to another computer over a network. Whether you are using Netty as client or server, all data exchanged with other computers on the network are passed through a SocketChannel instance representing the TCP connection between the computers.
A SocektChannel is managed by an EventLoop, and always only by that same EventLoop. Since an EventLoop is always executed by the same thread, a SocketChannel instance is also only accessed by the same thread. Therefore you don’t have to worry about synchronization when reading from a SocketChannel.

*ChannelInitializer

A Netty ChannelInitializer is a special ChannelHandler which is attached to the ChannelPipeline of a SocketChannel when the SocketChannel is created. The ChannelInitializer is then called so it can initialize the SocketChannel. After initializing the SocketChannel the ChannelInitializer removes itself from the ChannelPipeline

*ChannelPipeline

Each Netty SocketChannel has a ChannelPipeline. The ChannelPipeline contains a list of ChannelHandler instances. When the EventLoop reads data from a SocketChannel the data is passed to the first ChannelHandler in the ChannelPipeline. The first ChannelHandler processes the data and can choose to forward it to the next ChannelHandler in the ChannelPipeline, which then also processes the data and can choose to forward it to the next ChannelHandler in the ChannelPipeline etc.
When writing data out to a SocketChannel the written data is also passed through the ChannelPipeline before finally being written to the SocketChannel. Exactly how this works will be described in a separate tutorial.

*ChannelHandler

A Netty ChannelHandler handles the data that is received from a Netty SocketChannel. A ChannelHandler can also handle data that is being written out to a SocketChannel. Exactly how this works will be described in a separate tutorial.

bootstrap :分为服务端和客户端, 服务端的对象为ServerBootStrap   ,客户端的为BootStrap  作用开启线程和创建socket.

EventLoopGroup: 多个EventLoop 放到一个组合里面, 就想java的线程里面的ThreadGroup一样。

EventLoop: 一个回路(环状的) 放的是新的事件,netty是基于事件驱动的模型

SocketChannel: 代表通过网络连接到其他计算机的tcp, 无论是客户端还是服务端 所有的数据都是通过channel 来交换的。

ChannelInitializer: 是一个特殊的ChannelHandler, 当socketChannel被创建后该对象将附属于socketChannel 的pipeline. 
所以当创建socketchannel被实例化的时候,这个channelInitalizer就会被调用。
   
ChannelPipeline:每个socketchannel 都有一个channelPipeline ,而pipeline又包含一些列的channelHander实例。
当事件环从socketChanel读取数据的时候, 数据会被传递到第一个channelHandler -->然后会有通过pipeline 传递到
下一个channelHandler...  经过所有的channelHandler 
   
channelHandler: 处理业务代码的地方, 基于不同的事件。


ChannelHandler调用的链代码如下:
    class ChannelInboundHandlerAdapter{
       public void channelActive(ChannelHandlerContext ctx) throws Exception {
           ctx.fireChannelActive();  // 
        } 
        
        //......
        
        static void invokeChannelRegistered(final AbstractChannelHandlerContext next) {
            EventExecutor executor = next.executor();
            if (executor.inEventLoop()) {
                next.invokeChannelRegistered();
            } else {
                executor.execute(new Runnable() {
                    public void run() {
                        next.invokeChannelRegistered();
                    }
                });
            }
        
        }
    }

使用Netty创建客户端&服务端

客户发起一个消息到服务端, 然后服务端处理消息,返回一个处理的消息
/**
 * @author shenzm
 * @date 2019-1-17
 * @description 服务端
 */
public class NettyTcpServer {


    public static void main(String[] args) {
        EventLoopGroup childGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(childGroup)
                    .channel(NioServerSocketChannel.class)
                    .localAddress(new InetSocketAddress("localhost", 9999))
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            channel.pipeline().addLast(new HelloServerHandler());
                        }
                    });
            ChannelFuture channelFuture = serverBootstrap.bind().sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                childGroup.shutdownGracefully().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

public class HelloServerHandler extends ChannelInboundHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String body = ByteUtil.byteBuf2String((ByteBuf) msg);
        System.out.println("服务端获取的消息:" + body);
        ctx.writeAndFlush(Unpooled.copiedBuffer("服务端已接受到消息".getBytes()));

    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
    }

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

/**
 * @author shenzm
 * @date 2019-1-17
 * @description 客户端
 */
public class NettyTcpClient {

    public static void main(String[] args) {
        EventLoopGroup childGroup = new NioEventLoopGroup();
        try {
            Bootstrap clientBootStrap= new Bootstrap();
            clientBootStrap.group(childGroup)
                    .channel(NioSocketChannel.class)
                    .remoteAddress(new InetSocketAddress("localhost", 9999))
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel channel) throws Exception {
                            channel.pipeline().addLast(new ClientHandler());
                        }
                    });
            ChannelFuture channelFuture = clientBootStrap.connect().sync();
            channelFuture.channel().closeFuture().sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            try {
                childGroup.shutdownGracefully().sync();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }


    }
}

/**
 * @author shenzm
 * @date 2019-1-17
 * @description 作用
 */
public class ClientHandler extends SimpleChannelInboundHandler {

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer("Netty rocks!".getBytes()));
    }

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

    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception {
        String body = ByteUtil.byteBuf2String((ByteBuf) msg);
        System.out.println("客户端接服务端返回消息为:" + body);
    }


}

/**
 * @author shenzm
 * @date 2019-1-17
 * @description 作用
 */
public class ByteUtil {

    /**
     * ByteBuf 2 String
     *
     * @param buf
     * @return
     * @throws UnsupportedEncodingException
     */
    public static String byteBuf2String(ByteBuf buf) throws UnsupportedEncodingException {
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);
        return new String(bytes, "UTF-8");
    }
}

源码在:https://gitee.com/ninuxGithub/spring-boot-study.git

reference

http://tutorials.jenkov.com/netty/overview.html
https://blog.csdn.net/linuu/column/info/enjoynetty netty的系列博客
李林锋的《Netty权威指南》