Netty学习篇三——netty自定义编码解码器
本篇将自定义 编码解码器,对数据传输过程进行“入站解码,出站编码”。
- 服务端接收的是字节数据,通过“入站解码”,得到知道格式的数据;
- 服务器发送指定格式数据通过 “出站编码” 转换成字节数据,然后发送给客户端;
- 客户端类似;
- ChannelPipeLine 管理一系列 ChannelHandler,入站消息解码后转发给下一个 handler 进行处理
案例需求:客户端或服务器发送 Long 类型数据,出站编码成字节数据,入站解码读取对方发送的消息
编码器 MyLongToByteEncoder
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
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
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(); } } }
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()); } }
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
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(); } } }
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()); } }
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