Netty之自定义编码器MessageToMessageEncoder类

本文介绍了如何在Netty中实现字符串数据的编码与解码,包括自定义编码器MyStringEncoder和解码器MyStringDecoder的具体实现。此外,还详细展示了服务端与客户端的实现过程,以及如何通过这些组件实现字符串数据的发送与接收。

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

1、对于MessageToMessageEncoder的理解

         MessageToMessageEncoder编码器,这里的第二个Message可以理解为任意一个对象。如果是使用ByteBuf对象的话,就和之前的MessageToByte的原理是一样的了。所以要在MessageToMessageDecoder<ByteBuf>的解码器里面,手动的指定,是对ByteBuf类型的对象进行解码的操作。

2、编写MyStringEncoder编码器和MyStringDecoder解码器,以便于,Netty中可以直接发送和接收String类型的数据

     2.1  MyStringEncoder编码器的代码

  1. import io.netty.buffer.ByteBufUtil;  
  2. import io.netty.channel.ChannelHandlerContext;  
  3. import io.netty.handler.codec.MessageToMessageEncoder;  
  4. import java.nio.CharBuffer;  
  5. import java.nio.charset.Charset;  
  6. import java.util.List;  
  7.   
  8. public class MyStringEncoder extends MessageToMessageEncoder<CharSequence> {  
  9.     private final Charset charset;  
  10.   
  11.     public MyStringEncoder() {  
  12.         this(Charset.defaultCharset());  
  13.     }  
  14.   
  15.     public MyStringEncoder(Charset charset) {  
  16.         if (charset == null) {  
  17.             throw new NullPointerException(“charset”);  
  18.         }  
  19.         this.charset = charset;  
  20.     }  
  21.   
  22.     protected void encode(ChannelHandlerContext ctx, CharSequence msg,  
  23.             List<Object> out) throws Exception {  
  24.         if (msg.length() == 0) {  
  25.             return;  
  26.         }  
  27.   
  28.         out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg),  
  29.                 this.charset));  
  30.     }  
  31. }  
import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.util.List;

public class MyStringEncoder extends MessageToMessageEncoder<CharSequence> {
    private final Charset charset;

    public MyStringEncoder() {
        this(Charset.defaultCharset());
    }

    public MyStringEncoder(Charset charset) {
        if (charset == null) {
            throw new NullPointerException("charset");
        }
        this.charset = charset;
    }

    protected void encode(ChannelHandlerContext ctx, CharSequence msg,
            List<Object> out) throws Exception {
        if (msg.length() == 0) {
            return;
        }

        out.add(ByteBufUtil.encodeString(ctx.alloc(), CharBuffer.wrap(msg),
                this.charset));
    }
}

     2.2  MyStringDecoder解码器的代码

  1. import java.nio.charset.Charset;  
  2. import java.util.List;  
  3. import io.netty.buffer.ByteBuf;  
  4. import io.netty.channel.ChannelHandlerContext;  
  5. import io.netty.handler.codec.MessageToMessageDecoder;  
  6.   
  7. public class MyStringDecoder extends MessageToMessageDecoder<ByteBuf> {  
  8.     private final Charset charset;  
  9.   
  10.     public MyStringDecoder() {  
  11.         this(Charset.defaultCharset());  
  12.     }  
  13.   
  14.     public MyStringDecoder(Charset charset) {  
  15.         if (charset == null) {  
  16.             throw new NullPointerException(“charset”);  
  17.         }  
  18.         this.charset = charset;  
  19.     }  
  20.   
  21.     protected void decode(ChannelHandlerContext ctx, ByteBuf msg,  
  22.             List<Object> out) throws Exception {  
  23.         out.add(msg.toString(this.charset));  
  24.     }  
  25. }  
import java.nio.charset.Charset;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;

public class MyStringDecoder extends MessageToMessageDecoder<ByteBuf> {
    private final Charset charset;

    public MyStringDecoder() {
        this(Charset.defaultCharset());
    }

    public MyStringDecoder(Charset charset) {
        if (charset == null) {
            throw new NullPointerException("charset");
        }
        this.charset = charset;
    }

    protected void decode(ChannelHandlerContext ctx, ByteBuf msg,
            List<Object> out) throws Exception {
        out.add(msg.toString(this.charset));
    }
}
3、服务端的实现

  1. import io.netty.bootstrap.ServerBootstrap;  
  2. import io.netty.channel.ChannelFuture;  
  3. import io.netty.channel.ChannelInitializer;  
  4. import io.netty.channel.ChannelOption;  
  5. import io.netty.channel.EventLoopGroup;  
  6. import io.netty.channel.nio.NioEventLoopGroup;  
  7. import io.netty.channel.socket.SocketChannel;  
  8. import io.netty.channel.socket.nio.NioServerSocketChannel;  
  9.   
  10. public class Server {  
  11.     public void bind(int port) throws Exception {  
  12.         // 服务器线程组 用于网络事件的处理 一个用于服务器接收客户端的连接  
  13.         // 另一个线程组用于处理SocketChannel的网络读写  
  14.         EventLoopGroup bossGroup = new NioEventLoopGroup();  
  15.         EventLoopGroup workerGroup = new NioEventLoopGroup();  
  16.         try {  
  17.             // NIO服务器端的辅助启动类 降低服务器开发难度  
  18.             ServerBootstrap serverBootstrap = new ServerBootstrap();  
  19.             serverBootstrap.group(bossGroup, workerGroup)  
  20.                     .channel(NioServerSocketChannel.class)// 类似NIO中serverSocketChannel  
  21.                     .option(ChannelOption.SO_BACKLOG, 1024)// 配置TCP参数  
  22.                     .option(ChannelOption.SO_BACKLOG, 1024// 设置tcp缓冲区  
  23.                     .option(ChannelOption.SO_SNDBUF, 32 * 1024// 设置发送缓冲大小  
  24.                     .option(ChannelOption.SO_RCVBUF, 32 * 1024// 这是接收缓冲大小  
  25.                     .option(ChannelOption.SO_KEEPALIVE, true// 保持连接  
  26.                     .childHandler(new ChildChannelHandler());// 最后绑定I/O事件的处理类  
  27.                                                                 // 处理网络IO事件  
  28.   
  29.             // 服务器启动后 绑定监听端口 同步等待成功 主要用于异步操作的通知回调 回调处理用的ChildChannelHandler  
  30.             ChannelFuture f = serverBootstrap.bind(port).sync();  
  31.             System.out.println(”Server启动”);  
  32.             // 等待服务端监听端口关闭  
  33.             f.channel().closeFuture().sync();  
  34.   
  35.         } finally {  
  36.             // 优雅退出 释放线程池资源  
  37.             bossGroup.shutdownGracefully();  
  38.             workerGroup.shutdownGracefully();  
  39.             System.out.println(”服务器优雅的释放了线程资源…”);  
  40.         }  
  41.   
  42.     }  
  43.   
  44.     /** 
  45.      * 网络事件处理器 
  46.      */  
  47.     private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {  
  48.         @Override  
  49.         protected void initChannel(SocketChannel ch) throws Exception {  
  50.             // 增加自定义的编码器和解码器  
  51.             ch.pipeline().addLast(new MyStringEncoder());  
  52.             ch.pipeline().addLast(new MyStringDecoder());  
  53.             // 服务端的处理器  
  54.             ch.pipeline().addLast(new ServerHandler());  
  55.         }  
  56.     }  
  57.   
  58.     public static void main(String[] args) throws Exception {  
  59.         int port = 9998;  
  60.         new Server().bind(port);  
  61.     }  
  62. }  
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;

public class Server {
    public void bind(int port) throws Exception {
        // 服务器线程组 用于网络事件的处理 一个用于服务器接收客户端的连接
        // 另一个线程组用于处理SocketChannel的网络读写
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            // NIO服务器端的辅助启动类 降低服务器开发难度
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)// 类似NIO中serverSocketChannel
                    .option(ChannelOption.SO_BACKLOG, 1024)// 配置TCP参数
                    .option(ChannelOption.SO_BACKLOG, 1024) // 设置tcp缓冲区
                    .option(ChannelOption.SO_SNDBUF, 32 * 1024) // 设置发送缓冲大小
                    .option(ChannelOption.SO_RCVBUF, 32 * 1024) // 这是接收缓冲大小
                    .option(ChannelOption.SO_KEEPALIVE, true) // 保持连接
                    .childHandler(new ChildChannelHandler());// 最后绑定I/O事件的处理类
                                                                // 处理网络IO事件

            // 服务器启动后 绑定监听端口 同步等待成功 主要用于异步操作的通知回调 回调处理用的ChildChannelHandler
            ChannelFuture f = serverBootstrap.bind(port).sync();
            System.out.println("Server启动");
            // 等待服务端监听端口关闭
            f.channel().closeFuture().sync();

        } finally {
            // 优雅退出 释放线程池资源
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
            System.out.println("服务器优雅的释放了线程资源...");
        }

    }

    /**
     * 网络事件处理器
     */
    private class ChildChannelHandler extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            // 增加自定义的编码器和解码器
            ch.pipeline().addLast(new MyStringEncoder());
            ch.pipeline().addLast(new MyStringDecoder());
            // 服务端的处理器
            ch.pipeline().addLast(new ServerHandler());
        }
    }

    public static void main(String[] args) throws Exception {
        int port = 9998;
        new Server().bind(port);
    }
}
4、服务端Handler的实现

  1. import io.netty.channel.ChannelHandlerAdapter;  
  2. import io.netty.channel.ChannelHandlerContext;  
  3.   
  4. public class ServerHandler extends ChannelHandlerAdapter {  
  5.   
  6.     @Override  
  7.     public void channelRead(ChannelHandlerContext ctx, Object msg)  
  8.             throws Exception {  
  9.         // 接受客户端的数据  
  10.         String body = (String) msg;  
  11.         System.out.println(”Client :” + body);  
  12.         // 服务端,回写数据给客户端  
  13.         // 直接回写整形的数据  
  14.         String data = ”Hello ,I am Server …”;  
  15.         ctx.writeAndFlush(data);  
  16.   
  17.     }  
  18.   
  19.     @Override  
  20.     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)  
  21.             throws Exception {  
  22.         ctx.close();  
  23.     }  
  24.   
  25.     @Override  
  26.     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {  
  27.         ctx.flush();  
  28.     }  
  29. }  
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;

public class ServerHandler extends ChannelHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        // 接受客户端的数据
        String body = (String) msg;
        System.out.println("Client :" + body);
        // 服务端,回写数据给客户端
        // 直接回写整形的数据
        String data = "Hello ,I am Server ...";
        ctx.writeAndFlush(data);

    }

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

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.flush();
    }
}
5、 客户端的实现

  1. import io.netty.bootstrap.Bootstrap;  
  2. import io.netty.channel.ChannelFuture;  
  3. import io.netty.channel.ChannelInitializer;  
  4. import io.netty.channel.ChannelOption;  
  5. import io.netty.channel.EventLoopGroup;  
  6. import io.netty.channel.nio.NioEventLoopGroup;  
  7. import io.netty.channel.socket.SocketChannel;  
  8. import io.netty.channel.socket.nio.NioSocketChannel;  
  9.   
  10. public class Client {  
  11.     /** 
  12.      * 连接服务器 
  13.      *  
  14.      * @param port 
  15.      * @param host 
  16.      * @throws Exception 
  17.      */  
  18.     public void connect(int port, String host) throws Exception {  
  19.         // 配置客户端NIO线程组  
  20.         EventLoopGroup group = new NioEventLoopGroup();  
  21.         try {  
  22.             // 客户端辅助启动类 对客户端配置  
  23.             Bootstrap b = new Bootstrap();  
  24.             b.group(group)//  
  25.                     .channel(NioSocketChannel.class)//  
  26.                     .option(ChannelOption.TCP_NODELAY, true)//  
  27.                     .handler(new MyChannelHandler());//  
  28.             // 异步链接服务器 同步等待链接成功  
  29.             ChannelFuture f = b.connect(host, port).sync();  
  30.             System.out.println(f);  
  31.             // 发送消息  
  32.             Thread.sleep(1000);  
  33.             f.channel().writeAndFlush(”777”);  
  34.             f.channel().writeAndFlush(”666”);  
  35.             Thread.sleep(2000);  
  36.             f.channel().writeAndFlush(”888”);  
  37.   
  38.             // 等待链接关闭  
  39.             f.channel().closeFuture().sync();  
  40.   
  41.         } finally {  
  42.             group.shutdownGracefully();  
  43.             System.out.println(”客户端优雅的释放了线程资源…”);  
  44.         }  
  45.   
  46.     }  
  47.   
  48.     /** 
  49.      * 网络事件处理器 
  50.      */  
  51.     private class MyChannelHandler extends ChannelInitializer<SocketChannel> {  
  52.         @Override  
  53.         protected void initChannel(SocketChannel ch) throws Exception {  
  54.             // 增加自定义的编码器和解码器  
  55.             ch.pipeline().addLast(new MyStringEncoder());  
  56.             ch.pipeline().addLast(new MyStringDecoder());  
  57.             // 客户端的处理器  
  58.             ch.pipeline().addLast(new ClientHandler());  
  59.         }  
  60.   
  61.     }  
  62.   
  63.     public static void main(String[] args) throws Exception {  
  64.         new Client().connect(9998“127.0.0.1”);  
  65.   
  66.     }  
  67. }  
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;

public class Client {
    /**
     * 连接服务器
     * 
     * @param port
     * @param host
     * @throws Exception
     */
    public void connect(int port, String host) throws Exception {
        // 配置客户端NIO线程组
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            // 客户端辅助启动类 对客户端配置
            Bootstrap b = new Bootstrap();
            b.group(group)//
                    .channel(NioSocketChannel.class)//
                    .option(ChannelOption.TCP_NODELAY, true)//
                    .handler(new MyChannelHandler());//
            // 异步链接服务器 同步等待链接成功
            ChannelFuture f = b.connect(host, port).sync();
            System.out.println(f);
            // 发送消息
            Thread.sleep(1000);
            f.channel().writeAndFlush("777");
            f.channel().writeAndFlush("666");
            Thread.sleep(2000);
            f.channel().writeAndFlush("888");

            // 等待链接关闭
            f.channel().closeFuture().sync();

        } finally {
            group.shutdownGracefully();
            System.out.println("客户端优雅的释放了线程资源...");
        }

    }

    /**
     * 网络事件处理器
     */
    private class MyChannelHandler extends ChannelInitializer<SocketChannel> {
        @Override
        protected void initChannel(SocketChannel ch) throws Exception {
            // 增加自定义的编码器和解码器
            ch.pipeline().addLast(new MyStringEncoder());
            ch.pipeline().addLast(new MyStringDecoder());
            // 客户端的处理器
            ch.pipeline().addLast(new ClientHandler());
        }

    }

    public static void main(String[] args) throws Exception {
        new Client().connect(9998, "127.0.0.1");

    }
}
6、客户端Handler的实现

  1. import io.netty.channel.ChannelHandlerAdapter;  
  2. import io.netty.channel.ChannelHandlerContext;  
  3. import io.netty.util.ReferenceCountUtil;  
  4.   
  5. public class ClientHandler extends ChannelHandlerAdapter {  
  6.   
  7.     @Override  
  8.     public void channelRead(ChannelHandlerContext ctx, Object msg)  
  9.             throws Exception {  
  10.         try {  
  11.             String body = (String) msg;  
  12.             System.out.println(”Client :” + body);  
  13.   
  14.             // 只是读数据,没有写数据的话  
  15.             // 需要自己手动的释放的消息  
  16.   
  17.         } finally {  
  18.             ReferenceCountUtil.release(msg);  
  19.         }  
  20.     }  
  21.   
  22.     @Override  
  23.     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause)  
  24.             throws Exception {  
  25.         ctx.close();  
  26.     }  
  27.   
  28. }  
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.util.ReferenceCountUtil;

public class ClientHandler extends ChannelHandlerAdapter {

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg)
            throws Exception {
        try {
            String body = (String) msg;
            System.out.println("Client :" + body);

            // 只是读数据,没有写数据的话
            // 需要自己手动的释放的消息

        } finally {
            ReferenceCountUtil.release(msg);
        }
    }

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

}
7、关于直接发送和接收String类型的编码

     7.1 直接发送String类型的数据


     7.2 直接接收String类型的数据




评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值