netty基础

HelloWorld

public class HelloServer {
    public static void main(String[] args) {
        new ServerBootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringDecoder());// 解码
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { // 自定义事件
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // 处理读事件

                                System.out.println(msg.toString());
                            }
                        });
                    }
                }).bind("localhost", 8080);
    }
}
public class HellClient {
    public static void main(String[] args) throws InterruptedException {
        new Bootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder());

                    }
                }).connect(new InetSocketAddress("localhost", 8080))
                .sync()
                .channel()
                .writeAndFlush("hello world")
        ;
    }
}

概念

  • 把channel立即为数据的通道
  • 把msg理解为流动的数据,最开始输入是ByteBuf,但经过pipeline的加工,会变成其他类型对象,最后输出又变成ByteBuf
  • 把handler理解为数据处理的工序
    • 工序有多道,合在一起就是pipeline,pipeline负责发布事件(读、读取完成)传播给每个handler,handler对自己感兴趣的事件进行处理(重写了相应事件处理方法)
    • handler分为Inbound(入站,写入数据)和Outbound(出战,写出数据)两类
  • 把eventloog理解为处理数据的工人
    • 工人可以管理多个channel的io操作,并且一旦工人负责了某个channel,就要负责到底(绑定)
    • 工人既可以执行io操作,也可以进行任务处理,每位工人有任务队列,队列里可以堆放多个channel的待处理任务,任务分为普通任务,定时任务
    • 工人按照pipeline的顺序,依次按照handler的规划处理数据,可以为每道工序指定不同的工人

EventLoop

EventLoop本质是一个单线程执行器(同时维护了一个Selector),里面有run方法处理channel上源源不断的的io事件

他的继承关系比较复杂

  • 一条线是继承自juc.ScheduleExecutorService因此包含了线程池中的所有方法
  • 另一条线是继承自netty自己的OrderedEventExecutor
    • 提供了boolean inEventLoop(Thread thread)方法判断一个线程是否属于此EventLoop
    • 提供了parent方法来看看自己属于哪个EventLoopGroup

EventLoopGroup是一组EventLoop,Channel一般会调用EventLoopGroup的register方法绑定其中一个EventLoop,后续这个Channel上的io事件都由此EventLoop来处理(保证了io事件处理时的线程安全

  • 继承自netty自己的EventExecutorGroup
    • 实现了Iterable接口提供遍历EventLoop的能力
    • 另有next方法获取集合的下一个EventLoop
@Slf4j
public class TestEventLoop {
    public static void main(String[] args) {
        // 1 创建事件循环组
        NioEventLoopGroup group = new NioEventLoopGroup(2);// io事件,普通任务,定时任务
        DefaultEventLoopGroup defaultGroup = new DefaultEventLoopGroup();// 普通任务,定时任务
        // 如果没有指定线程数 使用 系统核心数*2
//        System.out.println(NettyRuntime.availableProcessors());
//        System.out.println(Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)));
        // 2 获取下一个时间循环对象
        System.out.println(group.next());
        System.out.println(group.next());
        System.out.println(group.next());
        System.out.println(group.next());
        System.out.println(group.next());

        // 3 执行普通任务, 意义: 异步处理
        /*group.next().submit(new Runnable() {
            public void run() {
                try {
                    Thread.sleep(1000);
                    log.debug("ok");
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }
            }
        });
        log.info("main ---- ");*/
        // 4 定时任务
        group.next().scheduleAtFixedRate(new Runnable() {
            public void run() {
                log.info("每隔1s ok");
            }
        },3,1, TimeUnit.SECONDS);

    }
}

boss 和 worker

public class HelloServer {
    public static void main(String[] args) {
        new ServerBootstrap()
                // boss 处理 连接 worker 处理读写
                .group(new NioEventLoopGroup(),new NioEventLoopGroup(2))
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                System.out.println(ctx.channel().eventLoop());
                                ByteBuf buf = (ByteBuf) msg;
                                System.out.println(buf.toString(Charset.defaultCharset()));
                            }
                        });

                    }
                }).bind(9999);
    }
}

耗时操作再次分工

public class HelloServer {
    public static void main(String[] args) {
        final DefaultEventLoopGroup defaultEventLoopGroup = new DefaultEventLoopGroup();
        new ServerBootstrap()
                .group(new NioEventLoopGroup(), new NioEventLoopGroup())
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(
                                defaultEventLoopGroup, "default", new ChannelInboundHandlerAdapter() {
                                    @Override
                                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                        ByteBuf buf = (ByteBuf) msg;
                                        System.out.println(buf);
                                        System.out.println(ctx.channel().eventLoop());
                                        ctx.fireChannelRead(msg);
                                    }
                                }
                        ).addLast(new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                ByteBuf buf = (ByteBuf) msg;
                                System.out.println(ctx.channel().eventLoop());
                                System.out.println(buf);

                            }
                        });

                    }
                }).bind(9999);
    }
}

Channel

Channel 的主要作用

  • close()可以用来关闭channel
  • closeFuture()用来处理channel的关闭
    • sync方法作用是同步等待channel关闭
    • 而addListener方法是异步等待channel关闭
  • pipeline()方法添加处理器
  • write()方法将数据写入
  • writeAndFlush()方法将数据写入并刷出
@Slf4j
public class HelloClient {
    public static void main(String[] args) throws InterruptedException {
        ChannelFuture channelFuture = new Bootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder());

                    }
                })
                // 1 连接到服务器
                // 异步非阻塞,main 发起了调用,真正执行connect 是 nio线程
                .connect(new InetSocketAddress("127.0.0.1", 9999));
        
        //channelFuture.sync();
        // 无阻塞向下执行获取channel
        Channel channel = channelFuture.channel();

        log.info("{} ----------",channel);
        // 向服务端发送数据
        channel.writeAndFlush("hello world");
    }

}



// 此时是拿不到channel连接对象的

两种解决方式

  • sync方法作用是同步等待channel关闭
  • addListener方法是异步等待channel关闭
@Slf4j
public class HelloClient {
    public static void main(String[] args) throws InterruptedException {
        ChannelFuture channelFuture = new Bootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder());

                    }
                })
                // 1 连接到服务器
                // 异步非阻塞,main 发起了调用,真正执行connect 是 nio线程
                .connect(new InetSocketAddress("127.0.0.1", 9999));

        ///1.  使用sync方法同步处理结果
//        channelFuture.sync();
//        Channel channel = channelFuture.channel();
//        log.info("{} ----------",channel);
//        channel.writeAndFlush("hello world");


        ///2. 使用addListener(回掉对象)方法异步处理结果
        channelFuture.addListener(new ChannelFutureListener() {
            // 在nio 线程连接建立好之后,会调用operationComplete
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                Channel channel = channelFuture.channel();
                log.info("{} ----------", channel);
                channel.writeAndFlush("hello world");
            }
        });
    }

}

channel关闭之后优雅的做一些善后操作


@Slf4j
public class HelloClient {
    public static void main(String[] args) throws InterruptedException {
        ChannelFuture channelFuture = new Bootstrap()
                .group(new NioEventLoopGroup())
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        // 添加hanndler日志
                        ch.pipeline().addLast(new LoggingHandler(LogLevel.DEBUG));
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                .connect(new InetSocketAddress("localhost", 9999));

        Channel channel = channelFuture.sync().channel();

        new Thread( ()->{
            Scanner scanner = new Scanner(System.in);
            while (true) {
                String s = scanner.nextLine();

                if ("q".equals(s)) {
                    channel.close();
                    break;
                }
                channel.writeAndFlush(s);

            }
        },"input").start();

        ChannelFuture closedFuture = channel.closeFuture();
        /// 关闭之后要做的事,善后操作
       /* closedFuture.sync();
        log.info("处理关闭之后的事");*/


        closedFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                log.info("处理关闭之后的事");
                group.shutdownGracefully();// 不是立刻关闭, 优雅的关闭,哈哈哈
            }
        });
    }
}

netty 异步理解

请添加图片描述

netty 采用以下划分

请添加图片描述

要点:

  • 单线程没法异步提高效率,必须配合多线程、多核cpu才能发挥异步的优势
  • 吞吐量提升(单位时间内处理请求的数量)
  • 合理进行任务拆分,也是利用异步的关键

Future & Promise

Jdk future < netty future < promise

在异步处理事,经常用到这两个接口

首先要说明netty中的Future于jdk中国呢的Future同名,但是是两个接口,netty的Future继承自jdk的Future,而Promise又对netty Future进行了扩展

  • jdk Future只能同步等待任务结束(成功或失败)才能得到结果
  • netty Future可以同步等待,也可以异步得到结果,但是都要等待任务结束
  • netty Promise不仅有netty Future的功能,而且脱离了任务独立存在,只做为两个线程间传递结果的容器
名称jdknettypromise
cancel取消任务
isCanceled任务是否取消
isDone任务是否完成,不能区分成功失败
get获取任务结果,阻塞等
getNow获取任务结果,非阻塞,还未产生结果返回null
await等待,如果任务失败,不会抛异常,而是通过isSuccess判断
sync等待任务结束,如果失败,抛异常
isSuccess是否成功
cause获取失败信息,非阻塞,如果没有失败,返回null
addListener添加回掉,异步接受结果
setSuccess设置成功
setFailure设置失败
public class TestFuture {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        /*ExecutorService service = Executors.newCachedThreadPool();
        Future<String> future = service.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("执行结算");
                Thread.sleep(3000);
                return "hello";
            }
        });

        /// 同步等待结果
        String s = future.get();
        System.out.println(s);*/



       /* NioEventLoopGroup eventExecutors = new NioEventLoopGroup();

        EventLoop eventLoop = eventExecutors.next();

        Future<String> future = eventLoop.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                System.out.println("执行结算");
                Thread.sleep(3000);
                return "hello";
            }
        });
// 阻塞
//        String now = future.get();
// 阻塞
//        future.await();
//        String now = future.getNow();
// 异步非阻塞

        future.addListener(new GenericFutureListener<Future<? super String>>() {
            @Override
            public void operationComplete(Future<? super String> future) throws Exception {
                System.out.println(future.get());
            }
        });*/

        // promise
        DefaultEventLoopGroup group = new DefaultEventLoopGroup();
        EventLoop eventLoop = group.next();
        DefaultPromise<String> defaultPromise = new DefaultPromise<>(eventLoop);

        new Thread(() -> {
            System.out.println("计算");
             try {
                Thread.sleep(5000);
                System.out.println(1 / 0);
            } catch (InterruptedException e) {
                defaultPromise.setFailure(new Exception());
                throw new RuntimeException(e);
            }
            defaultPromise.setSuccess("hello");
        }).start();

        defaultPromise.addListener(new GenericFutureListener<Future<? super String>>() {
            @Override
            public void operationComplete(Future<? super String> future) throws Exception {
                System.out.println(future.get());
            }
        });

    }
}

Handler & Pipeline

ChannelHandler用来处理Channel上的各种事件,分为入站、出战两种。所有ChannelHandler被连成一串

执行顺序演示

@Slf4j
public class HelloServer {
    public static void main(String[] args) {
        final DefaultEventLoopGroup defaultEventLoopGroup = new DefaultEventLoopGroup();
        new ServerBootstrap()
                .group(new NioEventLoopGroup(), new NioEventLoopGroup())
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        // 总是 head - h1 - h2 - h3 - h4 - h5 - h6- tail
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast("h1",new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                log.info("------1-----------");
                                ByteBuf buf = (ByteBuf) msg;
                                System.out.println(ctx.channel().eventLoop());
                                System.out.println(buf.toString());
                                super.channelRead(ctx,msg);// 数据读传递 两者二选一    ctx.fireChannelRead(msg);

                            }
                        });
                        pipeline.addLast("h2",new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                log.info("------2-----------");
                                ByteBuf buf = (ByteBuf) msg;
                                System.out.println(ctx.channel().eventLoop());
                                System.out.println(buf.toString());
                                super.channelRead(ctx,msg);

                            }
                        });
                        pipeline.addLast("h3",new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                log.info("------3-----------");
                                ByteBuf buf = (ByteBuf) msg;
                                System.out.println(ctx.channel().eventLoop());
                                System.out.println(buf.toString());
                                ctx.channel().writeAndFlush("hello wlfds");
                                super.channelRead(ctx,msg);

                            }
                        });

                        pipeline.addLast("h4",new ChannelOutboundHandlerAdapter(){
                            @Override
                            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                                log.info("------4-----------");
                                super.write(ctx, msg, promise);
                            }
                        });

                        pipeline.addLast("h5",new ChannelOutboundHandlerAdapter(){
                            @Override
                            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                                log.info("------5-----------");
                                super.write(ctx, msg, promise);
                            }
                        });

                        pipeline.addLast("h6",new ChannelOutboundHandlerAdapter(){
                            @Override
                            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                                log.info("------6-----------");
                                super.write(ctx, msg, promise);
                            }
                        });

                    }
                }).bind(9999);
    }
}

初学者常犯错误

ctx.writeAndFlush()

ctx.channel().writeAndFlush(“hello wlfds”);

@Slf4j
public class HelloServer {
    public static void main(String[] args) {
        final DefaultEventLoopGroup defaultEventLoopGroup = new DefaultEventLoopGroup();
        new ServerBootstrap()
                .group(new NioEventLoopGroup(), new NioEventLoopGroup())
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        // 总是 head - h1 - h2 - h3 - h4 - h5 - h6- tail
                        ChannelPipeline pipeline = ch.pipeline();
                        pipeline.addLast("h1",new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                log.info("------1-----------");
                                ByteBuf buf = (ByteBuf) msg;
                                System.out.println(ctx.channel().eventLoop());
                                log.debug(buf.toString());
                                super.channelRead(ctx,msg);// 数据读传递 两者二选一    ctx.fireChannelRead(msg);

                            }
                        });
                        pipeline.addLast("h2",new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                log.info("------2-----------");
                                ByteBuf buf = (ByteBuf) msg;
                                System.out.println(ctx.channel().eventLoop());
                                System.out.println(buf.toString());
                                super.channelRead(ctx,msg);

                            }
                        });
                        pipeline.addLast("h4",new ChannelOutboundHandlerAdapter(){
                            @Override
                            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                                log.info("------4-----------");
                                super.write(ctx, msg, promise);
                            }
                        });
                        pipeline.addLast("h3",new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                log.info("------3-----------");
                                ByteBuf buf = (ByteBuf) msg;
                                System.out.println(ctx.channel().eventLoop());
                                System.out.println(buf.toString());
//                                ctx.writeAndFlush()// 从当前往前找
                                ctx.channel().writeAndFlush("hello wlfds");// 从当前往后找
                                super.channelRead(ctx,msg);

                            }
                        });



                        pipeline.addLast("h5",new ChannelOutboundHandlerAdapter(){
                            @Override
                            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                                log.info("------5-----------");
                                super.write(ctx, msg, promise);
                            }
                        });

                        pipeline.addLast("h6",new ChannelOutboundHandlerAdapter(){
                            @Override
                            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                                log.info("------6-----------");
                                super.write(ctx, msg, promise);
                            }
                        });

                    }
                }).bind(9999);
    }
}

Bytebuf

自动扩容

@Slf4j
public class TestByteBuf {
    public static void main(String[] args) {
        ByteBuf buf = ByteBufAllocator.DEFAULT.buffer();
        System.out.println(buf);
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < 300; i++) {
            sb.append("a");
        }
        buf.writeBytes(sb.toString().getBytes());
        System.out.println(buf);
    }
}

工具类

/**
     * 日志打印方法
     *
     * @param buffer 字节缓冲对象
     */
    private static void log(ByteBuf buffer) {
        int length = buffer.readableBytes();
        int rows = length / 16 + (length % 15 == 0 ? 0 : 1) + 4;
        StringBuilder buf = new StringBuilder(rows * 80 * 2)
                .append("read index:").append(buffer.readerIndex())
                .append(" write index:").append(buffer.writerIndex())
                .append(" capacity:").append(buffer.capacity())
                // io.netty.util.internal.StringUtil 里面静态变量 NEWLINE
                .append(NEWLINE);
        // io.netty.buffer.ByteBufUtil中的 静态方法
        appendPrettyHexDump(buf, buffer);
        System.out.println(buf.toString());
    }

堆内存与直接内存

/// 默认直接内存
ByteBuf buffered = ByteBufAllocator.DEFAULT.buffer(10);
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.heapBuffer(10);
ByteBuf directBuffer = ByteBufAllocator.DEFAULT.directBuffer(10);
  • 直接内存创建销毁代价昂贵,但是读写性能高(少一次内存复制),适合配合池化技术使用
  • 直接内存对GC压力小,不受GC回收管理,但也要注意及时主动释放

池化 VS 非池化

池化的最大意义在于可以重用ByteBuf,优点

  • 没有池化,每次创建新的ByteBuf
  • 有了池化,重用ByteBuf
  • 高并发下,池化功能更节约内存,减少内存溢出

池化默认开启

-Dio.netty.allocator.type={unpooled|pooled}

  • 4.1 以后,非Android平台默认启用池化实现,Android平台启用非池化实现
  • 4.1 之前,默认非池化

ByteBuf组成

请添加图片描述

扩容规则:

  • 如果写入后数据未超过512,则选择下一个16的整数倍,例如写入后大小为12,则扩容后capacity是16
  • 如果写入后数据大小超过512,则选择下一个2n,例如写入后大小为513,则扩容后capacity是210=1024
  • 扩容不能超过max capacity 会报错

Retain & Release

由于Netty中有堆外内存的ByteBuf实现,堆外内存最好是手动来释放,而不是等GC垃圾回收

  • UnpooledHeapByteBuf使用的是JVM内存,只需要等待GC回收即可
  • UnpooledDirecByteBuf使用的就是直接内存,需要特殊的方法来回收内存
  • PooledByteBuf和他的子类使用了池化技术,需要跟复杂的规则来回收内存

回收内存的源码实现,请关注下面方法的不同实现:

protected abstract void deallocate()

Netty 这里采用了引用计数来控制回收内存,每个ByteBuf都实现了ReferenceCounted接口

  • 每个ByteBuf对象的初始化为1
  • 调用release方法计数-1,如果计数为0,ByteBuf内存被回收
  • 调用retain方法计数+1,表示调用者没用完之前,其他handler机使用了release也不会造成回收
  • 当计数为0时,底层内存被回收,这时即使ByteBuf对象哈在,其各个方法均无法正常使用

用slice零拷贝的方式演示释放内存的最佳实践

slice 与原始的ByteBuf 公用一套内存

 public static void main(String[] args) {
        ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(10);

        buf.writeBytes("abcdefghij".getBytes());
        ByteBuf buf1 = buf.slice(0, 5);
        buf1.retain();

        ByteBuf buf2 = buf.slice(5, 5);
        buf2.retain();

        System.out.println(buf1.toString());
        System.out.println(buf2.toString());
        buf.release();
    }

duplicate

截取原始ByteBuf所有内存,并且没有max capacity的限制,也是与原始的ByteBuf使用同一块底层内存,只是读写指针是独立的

copy

会将底层内存数据进行深拷贝,因此无论读写,都与原始ByteBuf无关

ComponsiteBuffer

合并两个ByteBuf

public static void main(String[] args) {
        ByteBuf buf = ByteBufAllocator.DEFAULT.buffer();
        log(buf);
        buf.writeBytes("aaa".getBytes());
        ByteBuf buf1 = ByteBufAllocator.DEFAULT.buffer();
        log(buf1);
        buf.writeBytes("bbb".getBytes());
        CompositeByteBuf compositedBuffer = ByteBufAllocator.DEFAULT.compositeBuffer();
        compositedBuffer.addComponents(true,buf,buf1);
        System.out.println(compositedBuffer.toString());
        log(compositedBuffer);
   
    }

ByteBuf优势

  • 池化- 可以重用池中ByteBuf实例,更节约内存,减少内存溢出的可能
  • 读写指针分离,不需要向ByteBuf实例一样切换读写模式
  • 可以自动扩容
  • 支持链式调用,使用更流畅
  • 很多地方体现零拷贝,例如slice、duplicate、compositeByteBuf

简单通讯


@Slf4j
public class TestServer {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup boss = new NioEventLoopGroup();
        NioEventLoopGroup worker = new NioEventLoopGroup();
        ChannelFuture future = new ServerBootstrap()
                .group(boss, worker)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel channel) throws Exception {
                        channel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

                                ByteBuf buf = (ByteBuf) msg;
                                log.info("客户端数据 {}",buf.toString(CharsetUtil.UTF_8));
                                log.info("客户端地址 {}",ctx.channel().remoteAddress());

                                ctx.channel().writeAndFlush(buf);
                            }

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

                        });
                    }
                }).bind(8080);

        future.channel().closeFuture().addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                   boss.shutdownGracefully();
                   worker.shutdownGracefully();
            }
        });

    }
}
@Slf4j
public class TestClient {
    public static void main(String[] args) throws InterruptedException {
        NioEventLoopGroup worker = new NioEventLoopGroup();

        ChannelFuture channelFuture = new Bootstrap()
                .group(worker)
                .channel(NioSocketChannel.class)
                .handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel channel) throws Exception {
                        channel.pipeline().addLast(new ChannelInboundHandlerAdapter() {
                            @Override
                            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                                super.channelActive(ctx);
                                ctx .writeAndFlush(Unpooled.copiedBuffer("hello world", CharsetUtil.UTF_8));
                            }

                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                ByteBuf buf = (ByteBuf) msg;
                                log.info("客户端读取");
                                log.info("服务端发送的消息 {}", buf.toString(CharsetUtil.UTF_8));
                            }

                            @Override
                            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                                super.exceptionCaught(ctx, cause);
                                ctx.close();
                            }
                        });
                    }
                }).connect(new InetSocketAddress("localhost", 8080));

        channelFuture.channel().closeFuture().addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture channelFuture) throws Exception {
                worker.shutdownGracefully();
            }
        });
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值