Netty学习篇三——netty自定义编码解码器

本篇将自定义 编码解码器,对数据传输过程进行“入站解码,出站编码”。

  • 服务端接收的是字节数据,通过“入站解码”,得到知道格式的数据;
  • 服务器发送指定格式数据通过 “出站编码” 转换成字节数据,然后发送给客户端;
  • 客户端类似;
  • ChannelPipeLine 管理一系列 ChannelHandler,入站消息解码后转发给下一个 handler 进行处理

案例需求:客户端或服务器发送 Long 类型数据,出站编码成字节数据,入站解码读取对方发送的消息

 

编码器 MyLongToByteEncoder

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.oy.inboundandoutbound;
 
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
 
public class MyLongToByteEncoder extends MessageToByteEncoder<Long> {
    @Override
    protected void encode(ChannelHandlerContext ctx, Long msg, ByteBuf out) throws Exception {
        System.out.println("MyLongToByteEncoder encoder 被调用. msg: " + msg);
        out.writeLong(msg);
    }
}

解码器 MyByteToLongDecoder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.oy.inboundandoutbound;
 
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
 
import java.util.List;
 
public class MyByteToLongDecoder extends ByteToMessageDecoder {
    /**
     * decode() 会根据接收的数据,被调用多次,知道确定没有新的元素添加到list,
     * 或者是 ByteBuf 没有更多的可读字节为止。
     * 如果 list 不为空,就会将 list 的内容传递给下一个 handler
     * @param ctx 上下文对象
     * @param in 入站后的 ByteBuf
     * @param out 将解码后的数据传递给下一个 handler
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
        // long 类型 为 8 字节
        if (in.readableBytes() >= 8) {
            out.add(in.readLong());
        }
    }
}

Server 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.oy.inboundandoutbound.server;
 
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
 
public class Server {
    public static void main(String[] args) {
        NioEventLoopGroup boss = new NioEventLoopGroup(1);
        NioEventLoopGroup work = new NioEventLoopGroup();
 
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap
                    .group(boss, work)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new MyServerChannelInitializer());
            ChannelFuture future = serverBootstrap.bind(8004).sync();
            System.out.println("server started and listen " + 8004);
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            boss.shutdownGracefully();
            work.shutdownGracefully();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.oy.inboundandoutbound.server;
 
import com.oy.inboundandoutbound.MyByteToLongDecoder;
import com.oy.inboundandoutbound.MyLongToByteEncoder;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
 
public class MyServerChannelInitializer extends ChannelInitializer<SocketChannel> {
 
    protected void initChannel(SocketChannel socketChannel) throws Exception {
        /* 向管道加入处理器 */
        ChannelPipeline pipeline = socketChannel.pipeline();
 
        // 入站的 handler 进行解码
        pipeline.addLast("decoder", new MyByteToLongDecoder());
        // 添加一个出站的 handler 对数据进行编码
        pipeline.addLast("encoder", new MyLongToByteEncoder());
 
        // 添加自定义的处理器
        pipeline.addLast("MyServerHandler", new MyServerHandler());
 
    }
 
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.oy.inboundandoutbound.server;
 
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
 
public class MyServerHandler extends SimpleChannelInboundHandler<Long> {
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {
        System.out.println("从客户端读到的数据:" + msg);
    }
 
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        // 服务器返回 long 类型数据
        ctx.writeAndFlush(654321L);
    }
 
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

Client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.oy.inboundandoutbound.client;
 
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
 
public class Client {
    public static void main(String[] args) {
        NioEventLoopGroup group = new NioEventLoopGroup();
 
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap
                    .group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new MyClientChannelInitializer());
            ChannelFuture future = bootstrap.connect("127.0.0.1", 8004).sync();
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            group.shutdownGracefully();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.oy.inboundandoutbound.client;
 
import com.oy.inboundandoutbound.MyByteToLongDecoder;
import com.oy.inboundandoutbound.MyLongToByteEncoder;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.nio.NioSocketChannel;
 
public class MyClientChannelInitializer extends ChannelInitializer<NioSocketChannel> {
    @Override
    protected void initChannel(NioSocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
 
        // 入站的 handler 进行解码
        pipeline.addLast("decoder", new MyByteToLongDecoder());
        // 添加一个出站的 handler 对数据进行编码
        pipeline.addLast("encoder", new MyLongToByteEncoder());
 
        // 添加自定义 handler,处理业务逻辑
        pipeline.addLast(new MyClientHandler());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.oy.inboundandoutbound.client;
 
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
 
public class MyClientHandler extends SimpleChannelInboundHandler<Long> {
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Long msg) throws Exception {
        // 客户端读取服务器发送的 long 类型数据
        System.out.println("客户端读取服务器发送的, msg:" + msg);
    }
 
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        // 客户端发送 long 类型数据
        ctx.writeAndFlush(123456L);
    }
 
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        ctx.close();
    }
}

启动服务器和客户端程序, 控制台打印结果:  

 

posted @   江南大才子  阅读(1014)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程
历史上的今天:
2019-11-12 Redis---缓存雪崩,缓存穿透,缓存击穿,缓存预热概念及解决方案
点击右上角即可分享
微信分享提示