Códec Netty, el códec personalizado Netty resuelve el problema del desempaquetado de paquetes adhesivos, explicación detallada del proceso de ejecución del códec Netty

1. Descripción general del códec

1. Descripción general del códec

Cuando Netty envía o recibe un mensaje, se produce una conversión de datos. Los mensajes entrantes se decodifican (se convierten de bytes a otro formato, como objetos java); los mensajes salientes se codifican en bytes.

Netty proporciona una serie de códecs prácticos, todos los cuales implementan la interfaz ChannelInboundHadnler o ChannelOutboundHandler. En estas clases, el método channelRead ha sido anulado. Tomando la entrada como ejemplo, se llamará a este método para cada mensaje leído del canal de entrada. Posteriormente, llamará al método decode() proporcionado por el decodificador para decodificar y reenviar los bytes decodificados al siguiente ChannelInboundHandler en ChannelPipeline.

2. Diagrama de relación de clases de codificador

Todos los codificadores heredan la clase abstracta MessageToByteEncoder y necesitan reescribir su método de codificación.
inserte la descripción de la imagen aquí
Descubrimos que el codificador hereda ChannelOutboundHandlerAdapter, lo que indica que solo se realizará la operación de salida.

3. Diagrama de relación de clase de decodificador

Todos los decodificadores heredan la clase abstracta ByteToMessageDecoder y necesitan reescribir su método de decodificación.
inserte la descripción de la imagen aquí
Descubrimos que el decodificador hereda ChannelInboundHandlerAdapter, lo que significa que se ejecutará la operación de entrada.

2. Tome el códec como ejemplo para comprender la entrada y la salida

1. Lado del servidor


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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 MyServer {
    
    
    public static void main(String[] args) throws Exception{
    
    

        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();

        try {
    
    

            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup,workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
    
    
                            @Override
                            protected void initChannel(SocketChannel ch) throws Exception {
    
    
                            ChannelPipeline pipeline = ch.pipeline();//一会下断点

                            //入站的handler进行解码 MyByteToLongDecoder
                            pipeline.addLast(new MyByteToLongDecoder());
                            //出站的handler进行编码
                            pipeline.addLast(new MyLongToByteEncoder());
                            //自定义的handler 处理业务逻辑
                            pipeline.addLast(new MyServerHandler());
                            System.out.println("end");
                        }
            }); //自定义初始化pipeline

            ChannelFuture channelFuture = serverBootstrap.bind(7000).sync();
            channelFuture.channel().closeFuture().sync();

        }finally {
    
    
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}



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("从客户端" + ctx.channel().remoteAddress() + " 读取到long " + msg);
        //给客户端发送一个long
        ctx.writeAndFlush(98765L);
    }

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

2. Lado del cliente


import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
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 MyClient {
    
    
    public static void main(String[] args)  throws  Exception{
    
    

        EventLoopGroup group = new NioEventLoopGroup();

        try {
    
    

            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group).channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
    
    
                        @Override
                        protected void initChannel(SocketChannel ch) throws Exception {
    
    

                            ChannelPipeline pipeline = ch.pipeline();

                            //加入一个出站的handler 对数据进行一个编码
                            pipeline.addLast(new MyLongToByteEncoder());

                            //这时一个入站的解码器(入站handler )
                            pipeline.addLast(new MyByteToLongDecoder());
                            //加入一个自定义的handler , 处理业务
                            pipeline.addLast(new MyClientHandler());

                        }
            }); //自定义一个初始化类

            ChannelFuture channelFuture = bootstrap.connect("localhost", 7000).sync();

            channelFuture.channel().closeFuture().sync();

        }finally {
    
    
            group.shutdownGracefully();
        }
    }
}


import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

public class MyClientHandler  extends SimpleChannelInboundHandler<Long> {
    
    
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Long msg) throws Exception {
    
    

        System.out.println("服务器的ip=" + ctx.channel().remoteAddress());
        System.out.println("收到服务器消息=" + msg);

    }

    //重写channelActive 发送数据
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
    
    
        System.out.println("MyClientHandler 发送数据");
        ctx.writeAndFlush(123456L); //发送的是一个long
    }
}

3. Códec


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 encode 被调用");
        System.out.println("msg=" + msg);
        out.writeLong(msg);

    }
}


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 out 不为空,就会将list的内容传递给下一个 channelinboundhandler处理, 该处理器的方法也会被调用多次
     *
     * @param ctx 上下文对象
     * @param in 入站的 ByteBuf
     * @param out List 集合,将解码后的数据传给下一个handler
     * @throws Exception
     */
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    
    

        System.out.println("MyByteToLongDecoder 被调用");
        //因为 long 8个字节, 需要判断有8个字节,才能读取一个long
        if(in.readableBytes() >= 8) {
    
    
            out.add(in.readLong());
        }
    }
}

3. Ejecutar y ver los resultados

lado del cliente:

MyClientHandler envía datos
La codificación MyLongToByteEncoder se llama
msg=123456
MyByteToLongDecoder se llama
servidor ip=localhost/127.0.0.1:7000
mensaje del servidor recibido=98765

lado del servidor:

Se llamó a MyByteToLongDecoder para
leer desde client/127.0.0.1:58478 a long 123456
Se llamó a la codificación MyLongToByteEncoder con
msg=98765

De acuerdo con nuestros resultados de ejecución, simplemente podemos sacar la conclusión de que el proceso de ejecución es más o menos así:

inserte la descripción de la imagen aquí

4. Asuntos que requieren atención

El códec solo puede manejar los datos del tipo especificado. Si el tipo de datos no coincide, el códec se omitirá, pero los datos no se perderán.

Por ejemplo, si el cliente ctx.writeAndFlush(Unpooled.copiedBuffer(“abcdabcdabcdabcd”, CharsetUtil.UTF_8)) envía una cadena de 16 bits, aunque se ejecutará el método de codificación del codificador, no se codificará. Por lo tanto, cuando escribimos Encoder, debemos prestar atención a la consistencia del tipo de datos entrantes y el tipo de datos procesados.

Echemos un vistazo al método de escritura de MessageToByteEncoder:

// io.netty.handler.codec.MessageToByteEncoder#write
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
    
    
    ByteBuf buf = null;
    try {
    
    
        if (acceptOutboundMessage(msg)) {
    
    //判断当前msg 是不是应该处理的类型,如果是就处理,不是就跳过encode
            @SuppressWarnings("unchecked")
            I cast = (I) msg;
            buf = allocateBuffer(ctx, cast, preferDirect);
            try {
    
    
                encode(ctx, cast, buf);
            } finally {
    
    
                ReferenceCountUtil.release(cast);
            }

            if (buf.isReadable()) {
    
    
                ctx.write(buf, promise);
            } else {
    
    
                buf.release();
                ctx.write(Unpooled.EMPTY_BUFFER, promise);
            }
            buf = null;
        } else {
    
    
            ctx.write(msg, promise);
        }
    } catch (EncoderException e) {
    
    
        throw e;
    } catch (Throwable e) {
    
    
        throw new EncoderException(e);
    } finally {
    
    
        if (buf != null) {
    
    
            buf.release();
        }
    }
}

3. Otros códecs integrados de Netty

Netty incluye muchos códecs, que suelen ser suficientes para nuestro trabajo diario:

1, Decodificador de reproducción

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ReplayingDecoder;

import java.util.List;

public class MyByteToLongDecoder extends ReplayingDecoder<Void> {
    
    
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
    
    

        System.out.println("MyByteToLongDecoder2 被调用");
        //在 ReplayingDecoder 不需要判断数据是否足够读取,内部会进行处理判断
        out.add(in.readLong());
    }
}

ReplayingDecoder es fácil de usar, pero también tiene algunas limitaciones:
1. No todas las operaciones de ByteBuf son compatibles.Si se llama a un método no compatible, se lanzará una UnsupportedOperationException.
2. ReplayingDecoder puede ser un poco más lento que ByteToMessageDecoder en algunos casos. Por ejemplo, cuando la red es lenta y el formato del mensaje es complejo, el mensaje se dividirá en varios fragmentos y la velocidad disminuirá.

2. Otros codificadores

LineBasedFrameDecoder: esta clase también se usa dentro de Netty, que usa caracteres de control de fin de línea (\n o \r\n) como delimitadores para analizar datos.
DelimiterBasedFrameDecoder: use caracteres especiales personalizados como delimitadores de mensajes.
HttpObiectDecoder: un decodificador para datos HTTP.
LengthFieldBasedFrameDecoder: identifique el mensaje de paquete completo especificando la longitud, de modo que los paquetes fijos y los mensajes de medio paquete se puedan procesar automáticamente.

Los codificadores y decodificadores vienen en pares.

3. El códec incorporado maneja el problema de pegar y desempacar

Cuatro soluciones para que Netty resuelva problemas pegajosos y de desembalaje

4. Personaliza el códec para darte cuenta del problema de pegar y desempaquetar

Netty resuelve el problema del desempaquetado de paquetes adhesivos, y Netty utiliza un códec personalizado para resolver el problema del desempaquetado de paquetes adhesivos

Supongo que te gusta

Origin blog.csdn.net/A_art_xiang/article/details/130290528
Recomendado
Clasificación