使用 Netty 的长度前缀框架 - 无法设置 LengthFieldPrepender/Decoder

Length-prefix framing with Netty - can't setup LengthFieldPrepender/Decoder

在我简单的 udp netty 应用程序中,我正在尝试使用 netty 启用长度前缀框架。我正在使用一个通道来处理传入消息和发布新消息 (#send)。

public class Server {

    private static final int FRAME_LENGTH_PREFIX_LENGTH = 2;
    private static final int FRAME_MAX_LENGTH = 64 * 1024;

    private static final LengthFieldPrepender frameLengthPrefixPrepender =
        new LengthFieldPrepender(FRAME_LENGTH_PREFIX_LENGTH, true);
    private static final LengthFieldBasedFrameDecoder frameLengthPrefixDecoder =
        new LengthFieldBasedFrameDecoder(FRAME_MAX_LENGTH, 0, FRAME_LENGTH_PREFIX_LENGTH,
            -FRAME_LENGTH_PREFIX_LENGTH, FRAME_LENGTH_PREFIX_LENGTH, true);

    private final InetSocketAddress me;

    private final Channel serverChannel;

    private Server(ServerBuilder builder) {
        this.serverChannel = buildServerChannel(builder.inetAddress, builder.port, builder.channel,
            builder.eventLoopExecutors, builder.channelOptions, builder.loggerLevel);

        this.me = new InetSocketAddress(builder.inetAddress, builder.port);
    }

    private Channel buildServerChannel(InetAddress inetAddress, Integer port,
                                       Class<? extends Channel> channel, EventLoopGroup eventExecutors,
                                       Map<ChannelOption, Object> opts, LogLevel logLevel) {

        val b = new Bootstrap();

        b.group(eventExecutors)
            .channel(channel);
        opts.forEach(b::option);

        b.handler(new ChannelInitializer<DatagramChannel>() {
            @Override
            protected void initChannel(DatagramChannel ch) throws Exception {
                val pipe = ch.pipeline();

                if (logLevel != null)
                    pipe.addLast(new LoggingHandler(logLevel));

                // pipe.addLast(frameLengthPrefixDecoder); // disabled for testing purposes

                pipe.addLast(new SimpleChannelInboundHandler<DatagramPacket>() {

                    @Override
                    protected void channelRead0(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception {
                        ByteBuf buf = (ByteBuf) msg.copy().content();
                        byte[] req = new byte[buf.readableBytes()];
                        buf.readBytes(req);
                        String body = new String(req, CharsetUtil.UTF_8);
                        System.out.println("SimpleChannelInboundHandler<DatagramPacket> : "+body);
                    }
                });

                pipe.addLast(frameLengthPrefixPrepender);
            }
        });
        return b.bind(inetAddress, port).syncUninterruptibly().channel();
    }

    private ChannelFuture send(InetSocketAddress dest, ByteBuf data) {
        val datagramPacket = new DatagramPacket(data, dest, me);

        return serverChannel.writeAndFlush(datagramPacket);
    }
}

我正在尝试发送 "Hello" 消息:

public static void main(String[] a) throws UnknownHostException {
    Server build = new Server().loggerLevel(LogLevel.DEBUG).build();
    build.send(new InetSocketAddress(InetAddress.getByName("localhost"), 1080), Unpooled.copiedBuffer("HELLO".getBytes()));
}

我期望的是:

+-------------------------------------------------+
|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 00 07 48 45 4c 4c 4f                            |..HELLO         |
           ^^^^^ prefix
+--------+-------------------------------------------------+----------------+

尽管如此,它仍然是:

+-------------------------------------------------+
|  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 48 45 4c 4c 4f                                  |HELLO           |
+--------+-------------------------------------------------+----------------+

我还尝试通过放置断点 LengthFieldPrepender#encode and LengthFieldBasedFrameDecoder#decode 来调试 LengthFieldPrepender/LengthFieldBasedFrameDecoder,但发现这些方法根本没有被调用。

我用这种方法制作了几个 hello world netty tcp 应用程序,从来没有遇到过问题。我觉得有一个非常简单的解释我做错了什么,但我正在努力寻找它。

这里的问题是您直接将 DatagramPacket 写入通道的管道,而出站处理程序 LengthFieldPrepender 只接受 ByteBuf 类型的对象。因此,数据报会跳过编码器。