Серия Netty: используйте POJO для замены buf

Java задняя часть Netty

Это 9-й день моего участия в августовском испытании обновлений.Подробности о мероприятии:Испытание августовского обновления

Введение

В предыдущей статье мы упоминали, что для NioSocketChannel он не получает самые основные строковые сообщения, а только ByteBuf и FileRegion. Однако ByteBuf обрабатывается в бинарном виде, что слишком неинтуитивно для программистов, и обрабатывать его сложнее.Возможна ли прямая обработка простых java-объектов? В этой статье будет рассмотрен этот вопрос.

декодировать и кодировать

Например, нам нужно записать строку прямо в канал, в предыдущей статье мы знали, что это невозможно, и будет выдано следующее сообщение об ошибке:

DefaultChannelPromise@57f5c075(failure: java.lang.UnsupportedOperationException: unsupported message type: String (expected: ByteBuf, FileRegion))

То есть ChannelPromise принимает только ByteBuf и FileRegion, так как же это сделать?

Поскольку ChannelPromise принимает только ByteBuf и FileRegion, нам нужно преобразовать объект String в ByteBuf.

Другими словами, преобразуйте String в ByteBuf перед записью String, а затем преобразуйте ByteBuf в String, когда данные должны быть прочитаны.

Мы знаем, что в ChannelPipeline можно добавить несколько обработчиков, и порядок этих обработчиков можно контролировать.

Потом появилась наша идея: добавить encode в ChannelPipeline, который используется для записи данных для кодирования данных в ByteBuf, а затем добавить decode для декодирования данных в соответствующие данные при записи данных.

Вы знакомы с кодированием и декодированием? Кстати, это сериализация объектов.

сериализация объекта

Сериализация объектов в netty заключается в преобразовании передаваемого объекта и ByteBuf напрямую друг в друга.Конечно, мы можем реализовать это преобразование объекта сами. Но netty предоставила нам два удобных класса преобразования: ObjectEncoder и ObjectDecoder.

Сначала посмотрите на ObjectEncoder, его роль заключается в преобразовании объектов в ByteBuf.

Этот класс очень простой, давайте разберем его:

public class ObjectEncoder extends MessageToByteEncoder<Serializable> {
    private static final byte[] LENGTH_PLACEHOLDER = new byte[4];

    @Override
    protected void encode(ChannelHandlerContext ctx, Serializable msg, ByteBuf out) throws Exception {
        int startIdx = out.writerIndex();

        ByteBufOutputStream bout = new ByteBufOutputStream(out);
        ObjectOutputStream oout = null;
        try {
            bout.write(LENGTH_PLACEHOLDER);
            oout = new CompactObjectOutputStream(bout);
            oout.writeObject(msg);
            oout.flush();
        } finally {
            if (oout != null) {
                oout.close();
            } else {
                bout.close();
            }
        }

        int endIdx = out.writerIndex();

        out.setInt(startIdx, endIdx - startIdx - 4);
    }
}

ObjectEncoder наследует MessageToByteEncoder, который, в свою очередь, наследует ChannelOutboundHandlerAdapter. Почему OutBound? Это потому, что мы хотим преобразовать записанный объект, чтобы он был исходящим.

Сначала используйте ByteBufOutputStream для инкапсуляции ByteBuf.В бою сначала напишите поле LENGTH_PLACEHOLDER, чтобы указать длину байта в потоке. Затем используйте CompactObjectOutputStream для инкапсуляции боя, и, наконец, вы можете использовать CompactObjectOutputStream для записи объекта.

Соответственно, у netty также есть объект ObjectDecoder, который используется для преобразования ByteBuf в соответствующий объект, ObjectDecoder наследуется от LengthFieldBasedFrameDecoder, по сути это ByteToMessageDecoder и ChannelInboundHandlerAdapter, который используется для обработки чтения данных.

Давайте рассмотрим наиболее важные методы декодирования в ObjectDecoder:

    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        ByteBuf frame = (ByteBuf) super.decode(ctx, in);
        if (frame == null) {
            return null;
        }

        ObjectInputStream ois = new CompactObjectInputStream(new ByteBufInputStream(frame, true), classResolver);
        try {
            return ois.readObject();
        } finally {
            ois.close();
        }
    }

Как видно из приведенного выше кода, входной ByteBuf преобразуется в ByteBufInputStream и, наконец, преобразуется в CompactObjectInputStream, и объект можно читать напрямую.

Работа с кодировщиками и декодерами

При использовании двух вышеуказанных кодеков вам необходимо добавить их непосредственно в ChannelPipeline на стороне клиента и сервера.

Для серверной части основной код выглядит следующим образом:

//定义bossGroup和workerGroup
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
             .channel(NioServerSocketChannel.class)
             .handler(new LoggingHandler(LogLevel.INFO))
             .childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline p = ch.pipeline();
                    p.addLast(
                            // 添加encoder和decoder
                            new ObjectEncoder(),
                            new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
                            new PojoServerHandler());
                }
             });

            // 绑定端口,并准备接受连接
            b.bind(PORT).sync().channel().closeFuture().sync();

Точно так же для клиентской стороны наш основной код выглядит следующим образом:

EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap b = new Bootstrap();
            b.group(group)
             .channel(NioSocketChannel.class)
             .handler(new ChannelInitializer<SocketChannel>() {
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline p = ch.pipeline();
                    p.addLast(
                            // 添加encoder和decoder
                            new ObjectEncoder(),
                            new ObjectDecoder(ClassResolvers.cacheDisabled(null)),
                            new PojoClientHandler());
                }
             });

            // 建立连接
            b.connect(HOST, PORT).sync().channel().closeFuture().sync();

Вы можете видеть, что приведенная выше логика заключается в добавлении ObjectEncoder и ObjectDecoder в ChannelPipeline.

Наконец, вы можете вызвать клиент и браузер:

ctx.write("加油!");

Пишите непосредственно в строковый объект.

Суммировать

С ObjectEncoder и ObjectDecoder мы не ограничены ByteBuf, и гибкость программы значительно улучшена.

Примеры этой статьи могут относиться к:learn-netty4

Эта статья была включена вWoohoo.Floyd Press.com/08-Netty-Break…

Самая популярная интерпретация, самая глубокая галантерея, самые краткие уроки и множество трюков, о которых вы не знаете, ждут вас!

Добро пожаловать, чтобы обратить внимание на мой официальный аккаунт: «Программируйте эти вещи», разбирайтесь в технологиях, лучше поймите себя!