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

时间:2017-06-24 09:24:03

标签: java networking network-programming udp netty

在我的简单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/LengthFieldBasedFrameDecoder来调试LengthFieldPrepender#encode and LengthFieldBasedFrameDecoder#decode,并发现这些方法根本没有被调用。

我以这种方式制作了几个hello world netty tcp应用程序并且从未出现过问题。我觉得有一个非常简单的解释我做错了什么,但我很难找到它。

1 个答案:

答案 0 :(得分:3)

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