# Netty 入门学习

Netty是一个基于NIO的异步事件驱动的网络应用框架,用于快速开发高性能服务器和客户端。文章详细介绍了Netty中的EventLoopGroup、EventLoop、Channel、Handler、ByteBuf等核心概念,以及如何使用Netty编写服务端和客户端程序,展示了Netty在处理IO事件和数据传输方面的高效性和灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

什么是netty?

Netty是一个异步事件驱动的网络应用框架,用于快速开发可维护的高性能服务器和客户端。是对JDK的NIO的封装。
这里的异步不是指异步非阻塞IO,而是指使用多线程将方法的调用与结果的返回分离开来,一个线程进行方法的调用,另一个线程进行结果的返回。

小试牛刀,用netty编写hello world

编写服务端

public class HelloServer {

    public static void main(String[] args) {
        //ServerBootstrap 服务启动器,负责组装netty组件,协调工作
        new ServerBootstrap()
                // BossEventLoop(selector,tread) ,workerEventLoop(selector,tread):事件循环处理器,包含一个selector和一个线程
                //通过selector去监听IO事件(连接、读取,写入等),同时采取多线程的方式,一个线程对应一个selector
                // NioEventLoopGroup:NIO的事件循环处理组,可能包含多个BossEventLoop和workerEventLoop               .group(new NioEventLoopGroup())
                .group(new NioEventLoopGroup())
                //选择ServerSocketChannel的实现
                .channel(NioServerSocketChannel.class)//支持BIO、NIO
                //boss负责处理连接 worker(child)负责处理读写
                //childHandler决定了将来的worker(child)能执行哪些操作(handler)
                .childHandler(
                        //Channel;代表和客户端进行数据读写的通道 Initializer:初始化,负责添加handler
                        new ChannelInitializer<NioSocketChannel>() { //在连接之后调用
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                         //添加handler
                        ch.pipeline().addLast(new StringDecoder());//对字节流ByteBuf转为字符串
                        //自定义handler
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
                           @Override   //处理读事件
                           public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                               System.out.println(msg);;
                           }
                       });
                    }
                    //服务器绑定的启动端口
                }).bind(8888);
    }
}

编写客户端

public class HelloClient {
    public static void main(String[] args)  throws InterruptedException{
             //1.添加启动器
        new Bootstrap()
                // 2. 添加 EventLoop, 包含线程 和 选择器
                .group(new NioEventLoopGroup())
                // 3. 选择客户端 channel 实现
                .channel(NioSocketChannel.class)
                // 4. 添加处理器
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        //5.对ByteBuf进行编码
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                // 6. 连接 服务器
                .connect(new InetSocketAddress("192.168.176.1", 8888))
                .sync()
                .channel()
                // 7.向服务器 发送信息
                .writeAndFlush("hello,world");
    }
}

代码执行流程
在这里插入图片描述

组件

1. NioEventLoop 事件循环对象

EventLoop 本质上是一个单线程的执行器(同时维护一个Selector来实现多路复用),当有事件发生时,通过调用内置的run方法来处理源源不断的IO事件
它的继承关系比较复杂

  • 一条线是继承自 j.u.c.ScheduledExecutorService 因此包含了线程池中所有的方法
  • 另一条线是继承自 netty 自己的 OrderedEventExecutor,可以有序的处理事件
    • 提供了 boolean inEventLoop(Thread thread) 方法判断一个线程是否属于此 EventLoop
    • 提供了 parent 方法来看看自己属于哪个 EventLoopGroup

EventLoopGroup 事件循环组

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

  • 继承自 netty 自己的 EventExecutorGroup
    • 实现了 Iterable 接口提供遍历 EventLoop 的能力
    • 另有 next 方法获取集合中下一个 EventLoop

EventLoopGroup 与EventLoop的使用

EventLoopGroup是一个接口,它的最强大的实现子类为NioEventLoopGroup,NioEventLoopGroup可以处理IO事件,普通事件、定时任务

NioEventLoopGroup的构造函数

EventLoopGroup eventLoopGroup1=new NioEventLoopGroup();
  /**
         * 对于无参构造函数new NioEventLoopGroup(),它会去调用this(0)方法,经过一系列的方法调用,最终会
         * 调用MultithreadEventLoopGroup方法,由于它没有传递参数,所以它会通过DEFAULT_EVENT_LOOP_THREADS
         * 来设置线程数,DEFAULT_EVENT_LOOP_THREADS会通过SystemPropertyUtil工具获取系统核心线程数,然后在NioEventLoopGroup
         * 内创建出2 * NettyRuntime.availableProcessors()的 EventLoop循环事件
         *public NioEventLoopGroup() {
         *         this(0);
         *     }
         * protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
         *         super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
         *     }
         *     private static final int DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
         */
EventLoopGroup eventLoopGroup2=new NioEventLoopGroup(2);
/**
 * 有参构造函数new NioEventLoopGroup(2);会创建2个EventLoop循环对象
*/

获取EventLoop对象的方法next

   EventLoopGroup eventLoopGroup2=new NioEventLoopGroup(2);
        
        System.out.println(eventLoopGroup2.next());
        System.out.println(eventLoopGroup2.next());
        System.out.println(eventLoopGroup2.next());
        System.out.println(eventLoopGroup2.next());

只设置了2个EventLoop,经过4次next获取,他回去轮询获取EventLoop对象
在这里插入图片描述
EventLoop执行普通任务

EventLoopGroup eventLoopGroup2=new NioEventLoopGroup(2);
        EventLoop eventLoop = eventLoopGroup2.next();
        eventLoop.submit(()->{
          log.debug(Thread.currentThread().getName()+"--->hello world");
        });

       log.debug("main");

在这里插入图片描述
EventLoop执行定时任务

 EventLoopGroup group=new NioEventLoopGroup(2);
        group.next().scheduleAtFixedRate(()->{
            log.debug(Thread.currentThread().getName()+"-->Hello world");
        },0,1,TimeUnit.SECONDS);

scheduleAtFixedRate(按固定的时间倍率刷新):0表示推迟0秒执行,1表示每隔一秒执行一次
结果
在这里插入图片描述
EventLoop执行IO任务
服务端

 public class HelloServer {


    public static void main(String[] args) {
        //ServerBootstrap 服务启动器,负责组装netty组件,协调工作
        new ServerBootstrap()
                // BossEventLoop(selector,tread) ,workerEventLoop(selector,tread):事件循环处理器,包含一个selector和一个线程
                //通过selector去监听IO事件(连接、读取,写入等),同时采取多线程的方式,一个线程对应一个selector
                // NioEventLoopGroup:NIO的事件循环处理组,可能包含多个BossEventLoop和workerEventLoop               .group(new NioEventLoopGroup())
                .group(new NioEventLoopGroup())
                //选择ServerSocketChannel的实现
                .channel(NioServerSocketChannel.class)//支持BIO、NIO
                //boss负责处理连接 worker(child)负责处理读写
                //childHandler决定了将来的worker(child)能执行哪些操作(handler)
                .childHandler(
                        //Channel;代表和客户端进行数据读写的通道 Initializer:初始化,负责添加handler
                        new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                         //添加handler
                        ch.pipeline().addLast(new StringDecoder());//对字节流ByteBuf转为字符串
                        //自定义handler
                        ch.pipeline().addLast(new ChannelInboundHandlerAdapter(){
                           @Override   //处理读事件
                           public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                               System.out.println(msg);;
                           }
                       });
                    }
                    //服务器绑定的启动端口
                }).bind(8888);
    }
}

客户端

public class HelloClient {
    public static void main(String[] args)  throws InterruptedException{
             //1.添加启动器
        new Bootstrap()
                // 2. 添加 EventLoop, 包含线程 和 选择器
                .group(new NioEventLoopGroup())
                // 3. 选择客户端 channel 实现
                .channel(NioSocketChannel.class)
                // 4. 添加处理器
                .handler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        //5.对ByteBuf进行编码
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                // 6. 连接 服务器
                .connect(new InetSocketAddress("192.168.176.1", 8888))
                .sync()
                .channel()
                // 7.向服务器 发送信息
                .writeAndFlush("hello,world");
    }
}

对EventLoop的分工细化

BOSS负责连接,Worker负责处理读写事件

 new ServerBootstrap()
 //第一个NioEventLoopGroup是BOSS负责连接,第二个是Worker负责处理读写
    .group(new NioEventLoopGroup(),new NioEventLoopGroup(2))
    ..........

当某个线程的 Channel事件处理时间过长,那么该线程关注的其他通道的事件就无法处理,可以将处理事件长的Channel事件交给其他的EventLoopGroup的EventLoop对象去处理。

 DefaultEventLoopGroup defaultGroup = new DefaultEventLoopGroup();
//在addLast时指定处理数据的EventLoopGroup,"handler"是组的名字
ch.pipeline().addLast(defaultGroup, "handler", new ChannelInboundHandlerAdapter() {
     @Override
     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
         log.debug((String) msg);
     }
});

完整代码

@Slf4j
public class HelloServer {


    public static void main(String[] args) {
        DefaultEventLoopGroup defaultGroup = new DefaultEventLoopGroup();
        new ServerBootstrap()
                //BOSS 负责连接                 worker负责处理读写事件
                .group(new NioEventLoopGroup(),new NioEventLoopGroup(2))
                .channel(NioServerSocketChannel.class)
                .childHandler(
                        new ChannelInitializer<NioSocketChannel>() {
                            @Override
                            protected void initChannel(NioSocketChannel ch) throws Exception {
                                ch.pipeline().addLast(new StringDecoder());
                                //指定其他的EventLoopGroup来处理此次Read事件
                                ch.pipeline().addLast(defaultGroup, "handler", new ChannelInboundHandlerAdapter() {
                                    @Override
                                    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                        log.debug((String) msg);
                                    }
                                });
                            }
                        })
                .bind(8888);
    }
}

在这里插入图片描述

2. Channel 通道

channel 的主要作用

  • close() 可以用来关闭 channel
  • closeFuture() 用来处理 channel 的关闭后的善后处理
    • sync 方法作用是同步等待 channel 关闭
    • 而 addListener 方法是异步等待 channel 关闭
  • pipeline() 方法添加处理器
  • write() 方法将数据写入
  • writeAndFlush() 方法将数据写入并刷出

ChannelFuture的连接问题
我们来看代码

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>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                .connect(new InetSocketAddress("localhost", 8888));
        channelFuture.sync();
        Channel channel = channelFuture.channel();
        channel .writeAndFlush("hello,world");
    }
}

为什么要调用channelFuture.sync()方法?
原因是connect连接是异步非阻塞的,调用connect的是main线程,真正执行连接的是nio线程,调用sync()方法,在还没有完成connect前将main线程阻塞,只有当nio线程完成connect时才会将main线程放行,确保客户端与服务端之间的Cannel建立连接

ChannelFuture对结果的处理

  • sync();阻塞main线程,把结果交给main来处理,属于同步阻塞处理
  • addListener();放行main线程,把结果的处理交给nio线程,当nio线程connect完成后,nio线程会处理结果并返回

代码实例

@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>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                .connect(new InetSocketAddress("localhost", 8888));
                // 异步非阻塞调用
           channelFuture.addListener(new ChannelFutureListener() {
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
                Channel channel = future.channel();
                log.debug("{}",channel);
                channel .writeAndFlush("hello,world");
            }
        });
    }
}

执行结果
在这里插入图片描述

ChannelFuture对关闭之后的善后处理
当我们的channle通道关闭之后,我们可能需要进行通道关闭之后的善后处理,包括但不限于对资源的释放等,这时哦我们就需要用到ChannelFuture对象
案例

@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>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                .connect(new InetSocketAddress("localhost", 8888));
        Channel channel = channelFuture.sync().channel();
        log.debug("{}",channel);
        new Thread(()->{
            channel.writeAndFlush("hello world");
            channel.close();//异步调用,由nio线程来执行关闭操作
           log.debug("close之后善后处理");
        },"MyThread").start();
    }
}

我们想要在关闭channel之后进行善后操作,如果直接进行善后操作这是不行的,因为channel.close();的关闭操作不是由MyThread线程执行,它是由nio线程来异步执行,而且关闭channel耗费的时间比较久,在关闭channel成功之前Mythread线程可能会先执行==log.debug(“close之后善后处理”);==操作,所以我们需要通过ChannelFuture来实现close之后的操作
ChannelFuture的方法
    1.sync();未完成channel关闭之前阻塞其他线程
    2.addListener(处理对象);由nio来执行关闭之后的善后处理操作,属于异步调用

@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>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        ch.pipeline().addLast(new StringEncoder());
                    }
                })
                .connect(new InetSocketAddress("localhost", 8888));
        Channel channel = channelFuture.sync().channel();
        log.debug("{}",channel);
        new Thread(()->{
            channel.writeAndFlush("hello world");
            channel.close();
        },"MyThread").start();

        ChannelFuture closeFuture = channel.closeFuture();
        //方法一
        closeFuture.sync();//在nio未完成关闭channel之前,阻塞main线程
        log.debug("close之后的善后处理......");
        //方法二
//        closeFuture.addListener(new ChannelFutureListener() {
//            @Override
//            public void operationComplete(ChannelFuture future) throws Exception {
//                log.debug("close之后的善后处理......");
//            }
//        });

    }

3. Future 与 Promise

当线程调用结束后会将结果返回给Future或Peomise,这两个类可以通过get方法获取返回的结果

       EventLoop eventLoop = new NioEventLoopGroup().next();
        Future<Integer> future = eventLoop.submit(() -> {
            return 70;
        });
        System.out.println(future.get());

对于Future的创建和往Future里面注入返回结果都是不可见的,Future是被动的创建与结果注入,而 Promise可以主动创建对象和注入返回结果

 public static void main(String[] args) throws Exception {
        EventLoop eventLoop = new NioEventLoopGroup().next();
        //主动创建对象
        DefaultPromise<Integer> promise = new DefaultPromise<>(eventLoop);
        new Thread(()->{
            try {
                log.debug("hello world");
                //主动注入结果
                promise.setSuccess(100);
            } catch (Exception e) {
                e.printStackTrace();
                 //主动注入结果
                promise.setFailure(e);
            }
        }).start();
        System.out.println(promise.get());
    }

4 Handler & Pipeline

ChannelHandler 用来处理 Channel 上的各种事件,分为入站、出站两种。所有 ChannelHandler 被连成一串,就是 Pipeline
入站的意思是客户端往服务端发送数据,服务器对数据的接收处理就是入站
出站的意思是服务端往客户端发送数据,服务端对返回数据进行加工处理就是出战

  • 入站处理器通常是 ChannelInboundHandlerAdapter 的子类,主要用来读取客户端数据,写回结果
  • 出站处理器通常是 ChannelOutboundHandlerAdapter 的子类,主要对写回结果进行加工

入站与出站的执行顺序
在往pipline加入handler的时候,会形成一条双向链表head<->h1<->h2<->tail,每个被添加的handler都会加入到链表的末尾,采取的是尾插法

@Override
protected void initChannel(NioSocketChannel ch) throws Exception {
 ch.pipeline().addLast(....);
 }

入战的执行顺序是从前往后,出战的执行顺序是从后往前
服务端

@Slf4j
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) {
                        ch.pipeline().addLast("h1",new ChannelInboundHandlerAdapter(){
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                System.out.println(1);
                                super.channelRead(ctx, msg);
                            }
                        });
                        ch.pipeline().addLast("h2",new ChannelInboundHandlerAdapter(){
                            @Override
                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                System.out.println(2);
                                super.channelRead(ctx, msg);
                                //写入数据
                                 ch.writeAndFlush(ctx.alloc().buffer().writeBytes("".getBytes(StandardCharsets.UTF_8)));
                            }
                        });
                        ch.pipeline().addLast("h3",new ChannelOutboundHandlerAdapter(){
                            @Override
                            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                                System.out.println(3);
                                super.write(ctx, msg, promise);
                            }
                        });
                        ch.pipeline().addLast("h4",new ChannelOutboundHandlerAdapter(){
                            @Override
                            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                                System.out.println(4);
                                super.write(ctx, msg, promise);
                            }
                        });
                    }
                })
                .bind(8080);
    }
}

在这里插入图片描述
1和2表示的是ChannelInboundHandlerAdapter的执行顺序,4和3表示的是ChannelOutboundHandlerAdapter 的执行顺序

5 .pipline是流水线,由多个handler组成,每个Handler将自己加工完的信息传递给下一个Handler,那产品是怎样传递的呢?

ChannelInboundHandlerAdapter的产品传递

ch.pipeline().addLast("h1",new ChannelInboundHandlerAdapter(){
		@Override
	 public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
           //获取信息
           ByteBuf byteBuf=(ByteBuf)msg;
           String name = byteBuf.toString(Charset.defaultCharset());
           log.debug(name);
           //将信息传递给h2处理器
           super.channelRead(ctx, name);
           /**super.channelRead(ctx, name);的底层实现,调用fireChannelRead方法将信息传递给下一个Handler
             * @Skip
             *     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
             *         ctx.fireChannelRead(msg);
             *     }
             */
		}
 });
 ch.pipeline().addLast("h2",new ChannelInboundHandlerAdapter(){
        @Override
       public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            String name = msg.toString();
            log.debug(name+"   java");
       }
});

关于ChannelOutboundHandlerAdapter处理器的触发顺序
当我们调用Channel对象的write()方法时会从双链表的tail从前往后去触发出站Handler,如果我们调用ChannelHandlerContext 的write()方法,会从当前的Handler从前往后去寻找出战Handler执行,因为Context对象属于当前Handler的上下文

  ch.pipeline().addLast("h2",new ChannelInboundHandlerAdapter(){
     @Override
       public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
           System.out.println(2);
           super.channelRead(ctx, msg);
           //写入数据
           ctx.channel().write(msg);
           ch.writeAndFlush(ctx.alloc().buffer().writeBytes("".getBytes(StandardCharsets.UTF_8)));
           ctx.writeAndFlush(ctx.alloc().buffer().writeBytes("".getBytes(StandardCharsets.UTF_8)));
       }
   });

在这里插入图片描述

ByteBuf 对字节数据的封装

在使用JDK的NIO时,我们创建了ByteBuffer来缓存数据,但是这个ByteBuffer是个定长数组,不可动态扩容,而netty提供的ByteBuf 是功能更为强大的缓冲区组件,可以实现数组的动态扩容

// ByteBuf的创建
  ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();//默认容量大小为256

ByteBuf的组成
在这里插入图片描述
通过读写指针,不用在通过compat或clear来切换读写模式,同时ByteBuf还支持动态扩容

ByteBuf之slice
【零拷贝】的体现之一,对原始 ByteBuf 进行切片成多个 ByteBuf,切片后的 ByteBuf 并没有发生内存复制,还是使用原始 ByteBuf 的内存,切片后的 ByteBuf 维护独立的 read,write 指针,使用的还是统一个ByteBuf
在这里插入图片描述

         ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(10);
        buf.writeBytes(new byte[]{'a','b','c','d','e','f','g','h','i','j'});
        log(buf);
        // 切片,没有发生数据复制
         ByteBuf f1 = buf.slice(0,5); //startIdx length
         ByteBuf f2 = buf.slice(5,5);
         log(f1);
         log(f2);
         //在f1切片对数据进行修改,原来的ByteBuf也发生了修改,所以他们是同一个ByteBuf
         f1.setByte(0,'Z');
         log(buf);

结果展示

在这里插入图片描述
在这里插入图片描述
ByteBuf之Composite
【零拷贝】的体现之一,可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf,避免拷贝

       ByteBuf f1 = ByteBufAllocator.DEFAULT.buffer();
        f1.writeBytes(new byte[]{1,2,3,4,5});
        ByteBuf f2 = ByteBufAllocator.DEFAULT.buffer();
        f2.writeBytes(new byte[]{6,7,8,9,10});
        //composite
        CompositeByteBuf compositeBuffer = ByteBufAllocator.DEFAULT.compositeBuffer();
        CompositeByteBuf buffer = compositeBuffer.addComponents(f1, f2);
        log(buffer);

EmbeddedChannel 测试工具类

Netty 提供了它所谓的 Embedded 传输,用于测试 ChannelHandler。这个传输是一种特殊的 Channel 实现— EmbeddedChannel— 的功能,这个实现提供了通过 ChannelPipeline 传播事件的简便方法。
测试代码

  @Test
    public void test2()
    {
       //定义入站处理器
        ChannelInboundHandlerAdapter in = new ChannelInboundHandlerAdapter() {
            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                ByteBuf byteBuf=(ByteBuf)msg;
                String s = byteBuf.toString(Charset.defaultCharset());
                System.out.println(s);


            }
        };
        //定义出站处理器
        ChannelOutboundHandlerAdapter out = new ChannelOutboundHandlerAdapter() {
            @Override
            public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
                ByteBuf byteBuf=(ByteBuf)msg;
                String s = byteBuf.toString(Charset.defaultCharset());
                System.out.println(s);
            }
        };

        EmbeddedChannel embeddedChannel=new EmbeddedChannel(in,out);
        //测试入站handler
        embeddedChannel.writeInbound(ByteBufAllocator.DEFAULT.buffer().writeBytes("入站处理".getBytes()));
        //测试出站handler
        embeddedChannel.writeOutbound(ByteBufAllocator.DEFAULT.buffer().writeBytes("出站处理".getBytes()));
    }

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值