• Netty(1-1)Discard


    一、DiscardServerHandler

    import io.netty.buffer.ByteBuf;
    import io.netty.channel.ChannelHandlerContext;
    import io.netty.channel.ChannelInboundHandlerAdapter;
    
    public class DiscardServerHandler extends ChannelInboundHandlerAdapter {//(1)
    
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {//(2)
            ByteBuf in = (ByteBuf) msg;
            //discard the received data silently
            in.release();//(3)
        }
    
        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {//(4)
            cause.printStackTrace();
            ctx.close();
        }
    
    }

    1、DiscardServerHandler extends ChannelInboundHandlerAdapter。ChannelInboundHandlerAdapter是ChannelInboundHandler的实现。ChannelInboundHandler提供了很多event handler的方法,需要你自行覆盖。但是,你不需要自己实现它,只需要extends ChannelInboundHandlerAdapter即可
    2、覆盖channelRead(),当收到消息时,该方法会被调用,来自客户端的任何数据都会被接收到。本例,接收的消息类型是ByteBuf。
    3、为了实现DISCARD PROTOCOL,该handler必须忽略掉接收到的消息。ByteBuf是reference-counted对象,必须被销毁,通过写release()。但是,如果该ByteBuffer,需要传递给下一个handler处理时,则不要release()。调用release()的场景:
    1)、谁是最后使用者,谁负责释放
    2)、如果有异常了,即ByteBuf没有成功传递到下一个Handler,一定要自行释放。
    通常channelRead()类似如下实现:

    try {
            // Do something with msg
        } finally {
            ReferenceCountUtil.release(msg);
        }

    4、exceptionCaught(),由于I/O异常或者处理events时异常会调用该方法。多数情况下,该方法内应该实现
    1)、logged(记录错误日志)
    2)、关闭掉相关的channel。
    当然,你可以在关闭该connection前,返回response消息给客户端。

    二、DiscardServer

    import io.netty.bootstrap.ServerBootstrap;
    import io.netty.channel.ChannelFuture;
    import io.netty.channel.ChannelInitializer;
    import io.netty.channel.ChannelOption;
    import io.netty.channel.EventLoopGroup;
    import io.netty.channel.nio.NioEventLoopGroup;
    import io.netty.channel.socket.SocketChannel;
    import io.netty.channel.socket.nio.NioServerSocketChannel;
    import lombok.extern.slf4j.Slf4j;
    
    /**
     * Discards any incoming data
     */
    @Slf4j
    public class DiscardServer {
        private int port;
    
        public DiscardServer(int port) {
            this.port = port;
        }
        
        public void run() throws InterruptedException {
            EventLoopGroup bossGroup = new NioEventLoopGroup();//(1)
            EventLoopGroup workerGroup = new NioEventLoopGroup();
            try {
                ServerBootstrap b = new ServerBootstrap();//(2)
                b.group(bossGroup,workerGroup)
                .channel(NioServerSocketChannel.class)//(3)
                .childHandler(new ChannelInitializer<SocketChannel>() {//(4)
                    @Override
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new DiscardServerHandler());
                    }
                })
                .option(ChannelOption.SO_BACKLOG, 128)//(5)
                .childOption(ChannelOption.SO_KEEPALIVE, true);//(6)
                //bind and start to accept incoming connections
                ChannelFuture f = b.bind(port).sync();//(7)
                log.info(DiscardServer.class.getName() + "started and listen on " + f.channel().localAddress());
                //wait unit the server socket is closed.
                //本例中,不会关闭,但以下能优雅关闭server
                f.channel().closeFuture().sync();//(8)
                //log.info("===================已关闭");
            } finally {
                workerGroup.shutdownGracefully();//(9)
                bossGroup.shutdownGracefully();
            }
        }
        public static void main(String[] args) throws InterruptedException {
            int port = 8080;
            new DiscardServer(port).run();
        }
    }

    1、NioEventLoopGroup是多线程的event loop,用于处理I/O操作。netty提供了多种EventLoopGroup,如:OioEventLoopGroup。服务端,建议使用2个NioEventLoopGroup。第一个,常叫做“boss”,接收connection,其实就是Acceptor线程池,建议线程数设置为1。第二个,常叫做“worker”,真正负责I/O读写操作的线程池,将connection注册到 该worker上,用于后续的Channel绑定。

    public NioEventLoopGroup() {
    this(0);
    }
    public NioEventLoopGroup(int nThreads) {
    this(nThreads, (Executor) null);
    }

    2、ServerBootstrap是一个帮助类,对于Channel,直接写即可,如:.channel(NioServerSocketChannel.class)

    3、使用NioServerSocketChannel接收connection
    4、每来一个connection,则会创建一个Channel,并且该ChannelInitializer将会新增一个DiscardServerHandler实例到该Channel的ChannelPipeline
    5、socket自带参数,参考ChannelOption和ChannelConfig
    6、option()用于NioServerSocketChannel接收connection。childOption()用于Channel
          5、6参见https://www.jianshu.com/p/0bff7c020af2
    7、以“异步方式”绑定server;sync()等待绑定的完成。
    8、
    1)关闭该Channel(断开connection) 并且,
    2)获取该Channel的CloseFuture,通过阻塞当前线程直到完成。
    9、关闭NioEventLoopGroup,并且释放掉所有资源

    三、测试

    使用telnet,

    telnet localhost 8080

    服务端没有任何动静,因为,没打印日志,且释放掉了。为了看得效果,修改DiscardServerHandler中的channelRead方法,如下:

    @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            ByteBuf in = (ByteBuf) msg;
            try {
                while (in.isReadable()) {//(1)
                    log.info("{}",(char)in.readByte());
                    System.out.flush();
                }
            } finally {
                ReferenceCountUtil.release(msg);//(2)
            }
        }

    然后,telnet

    Microsoft Telnet> send s1
    发送字符串 s1
    Microsoft Telnet>

    服务端收到:

    11:32:52.533 [nioEventLoopGroup-3-1] s
    11:32:52.535 [nioEventLoopGroup-3-1] 1

    1、这个效率低下的while,可以替换成:

    log.info(in.toString(io.netty.util.CharsetUtil.UTF_8));

    telnet后,输出:11:36:04.363 [nioEventLoopGroup-3-1] s1

    2、这里的release也可以使用in.release();

    继续telnet测试,在windows下发送中文,服务器接收到的是乱码。但是在linux下执行:

    [root@cent7-zuoys ~]# telnet 10.134.253.10 8080
    Trying 10.134.253.10...
    Connected to 10.134.253.10.
    Escape character is '^]'.
    遥远

    服务器打印:11:46:56.333 [nioEventLoopGroup-3-3] 遥远

    下一节,写netty client,代替telnet。

  • 相关阅读:
    Elementary Methods in Number Theory Exercise 1.5.2
    Elementary methods in number theory exercise 1.5.1 暨 重启C++之路:列出1到210的所有素数
    Elementary Methods in Number Theory Exercise 1.5.5
    《Elementary Methods in Number Theory》勘误
    Elementary Methods in Number Theory Exercise 1.5.2
    Elementary Methods in Number Theory Exercise 1.5.5
    Linux_我理解的逻辑地址、线性地址、物理地址和虚拟地址(补充完整了)
    寄存器和常用汇编指令
    Linux_AMD体系结构学习(内存模型)
    计算机是如何启动的?
  • 原文地址:https://www.cnblogs.com/yaoyuan2/p/9618350.html
Copyright © 2020-2023  润新知