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(); } } |
启动服务器和客户端程序, 控制台打印结果:
now ,fight for future
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于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---缓存雪崩,缓存穿透,缓存击穿,缓存预热概念及解决方案