Netty utiliza un decodificador de longitud para implementar el procesamiento de paquetes adhesivos de medio paquete

I. Resumen:

  1. Explicación del sustantivo

  Medio paquete: protocolo TCP, hay un búfer, si un paquete enviado es más grande que el tamaño del búfer, habrá un fenómeno de medio paquete, es decir, un paquete se divide en dos transmisiones.

  Paquetes pegajosos: También se debe a la existencia del área de búfer, si se envían muchos paquetes de pequeño tamaño en poco tiempo, la capa inferior de la implementación del protocolo TCP enviará estos pequeños paquetes juntos.

  2. Resumen

  En primer lugar, la aparición del paquete adhesivo semi-paquete no se debe al marco de la red, sino a un problema que surge de la implementación del protocolo TCP. Cómo resolver el paquete adhesivo y el medio paquete, existen principalmente los siguientes métodos en el marco netty que usamos.

  1) Establecer el delimitador final del paquete

  2) Se envía el paquete de longitud fija y se decodifica el paquete de longitud fija para eliminar los espacios cuando está en uso

  3) Agregue una broca de marca a la cabeza y retire la broca de marca cuando la use.

  3. Decodificadores integrados habituales

// (1) Use FixedLengthFrameDecoder para resolver el problema de los paquetes adhesivos de mensajes de longitud fija;
//
// (2) Use LineBasedFrameDecoder y StringDecoder para resolver el problema de los paquetes adhesivos TCP con retorno de carro y salto de línea como el final del mensaje ;
//
// (3) El decodificador delimitador especial DelimiterBasedFrameDecoder se usa para resolver el problema del paquete pegajoso TCP con símbolos especiales al final del mensaje; (Es un método personalizado de 2)
//
// (4) El último uno es también este artículo Utilice el decodificador de longitud personalizado LengthFieldBasedFrameDecoder para resolver el problema del paquete pegajoso TCP.

 

Dos, codificación y decodificación

1. Proceso de codificación y decodificación

El controlador personalizado del cliente envía la información, después del procesamiento de LengthProtocoltEncode (la posición es la penúltima), agrega el nombre del protocolo y el campo de longitud y lo envía al servidor. El servidor pasa por el decodificador heredado de LengthFieldBasedFrameDecoder ( la posición es la primera) Procesamiento automático, entregado al decodificador de campo de longitud Procesamiento de LenghtProtocoltDecode (la posición es la segunda), y finalmente entregado al último procesador en el lado del servidor para imprimir la entrada del cliente, y Echo devuelve los datos.

Nota: Si se ingresa la posición del codificador, entonces la entrada se procesa de adelante hacia atrás. Por lo tanto, después de decodificar la longitud, la longitud se elimina y la salida está en la dirección opuesta. Por lo tanto, el penúltimo debe procesar el contenido de salida real. (Además: si agrega un codificador de cadena, debe colocarse detrás del procesador personalizado, porque el procesador personalizado escribe datos y escribe String, que debe ser procesado por el codificador de cadena)

2. Reglas del acuerdo

  [Versión de protocolo de 2 longitudes, campo de longitud de 2 longitudes, contenido del paquete real]

3. Tres procesadores principales de codificación y decodificación

public class AllHandlers {

	
	/**
	 *
	 * 继承LengthFieldBasedFrameDecoder的解码器,主要是传入自定义的参数。
	 */
	public static class LengthPackageSpliter extends LengthFieldBasedFrameDecoder {
		
		/**
		 * 参数[1] : 长度字段偏移,参数[2] : 长度字段占用尺寸
		 */
		public LengthPackageSpliter() {
			super(Integer.MAX_VALUE, Constants.LENGTH_OFFSET, Constants.LENGTH_BYTES_COUNT);
		}

		@Override
		protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
			return super.decode(ctx, in);
		}
	}
	
	
	/**
	 * 消息编码器,用于发送消息后,对消息内容自动增加协议字段、长度字段。
	 */
	public static class LengthProtocoltEncode extends MessageToByteEncoder<ByteBuf> {
		
		@Override
		protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception {
			int length = msg.readableBytes();
			ByteBuf buff = Unpooled.buffer(Constants.PROTOCOL_HEADLENGTH + length);
			buff.writeShort(Constants.PROTOCOL_VERSION);
			buff.writeShort(length);
			buff.writeBytes(msg);
			out.writeBytes(buff);
		}
		
	}

//  消息解码器:方式1
//	public static class LenghtProtocoltDecode extends ChannelInboundHandlerAdapter {
//
//		@Override
//		public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//			// 去除长度字符串
//			ByteBuf buff = (ByteBuf) msg;
//			short vs = buff.readShort();
//			short length = buff.readShort();
//			ByteBuf buf = buff.readBytes(length);
//			super.channelRead(ctx, buf);
//		}
//	}

//  消息解码器:方式2
	public static class LenghtProtocoltDecode extends ByteToMessageDecoder {

		@Override
		protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
			short vs = in.readShort();
			short length = in.readShort();
			ByteBuf buf = in.readBytes(length);
			out.add(buf);
		}
	}

 Tres, implementación de código

1. Servidor

public class PkgServer {
	
	public static void main(String[] args) {
		new PkgServer().bind(7000);
	}
	
	public void bind(int port) {
		EventLoopGroup bossGroup = new NioEventLoopGroup(1);
		EventLoopGroup workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 100)
					.handler(new LoggingHandler(LogLevel.INFO)).childHandler(new ChannelInitializer<SocketChannel>() {
						@Override
						protected void initChannel(SocketChannel ch) throws Exception {
							ch.pipeline().addLast(new LengthPackageSpliter());
							ch.pipeline().addLast(new LenghtProtocoltDecode());
							ch.pipeline().addLast(new LengthProtocoltEncode());
							ch.pipeline().addLast(new ServerHandler());
						}
					});
			ChannelFuture future = b.bind(port).sync();
			System.out.println("server is start!");
			future.channel().closeFuture().sync();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}
	
	public class ServerHandler extends ChannelInboundHandlerAdapter {

		@Override
		public void channelActive(ChannelHandlerContext ctx) throws Exception {
			SocketChannel channel = (SocketChannel) ctx.channel();
			System.out.println("链接报告信息:有一客户端链接到本服务端");
			System.out.println("链接报告IP:" + channel.localAddress().getHostString());
			System.out.println("链接报告Port:" + channel.localAddress().getPort());
			System.out.println("链接报告完毕");
		}

		@Override
		public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
			ByteBuf buff = (ByteBuf) msg;
			byte[] readdata = new byte[buff.readableBytes()];
			buff.readBytes(readdata);
			String result = new String(readdata);
			System.out.println("server receive msg:" +result);
			String response = "echo" + result;
			buff = Unpooled.buffer(response.getBytes().length);
			buff.writeBytes(response.getBytes());
			ctx.writeAndFlush(buff);
		}
	}
	
}


2. Cliente

public class PkgClient {

	public static void main(String[] args) throws Exception {
		PkgClient client = new PkgClient();
		client.bind("127.0.0.1", 7000);
	}
	
	public void bind(String address, int port) throws Exception {
		EventLoopGroup loopGroup = new NioEventLoopGroup();
		try {
			Bootstrap b = new Bootstrap();
			b.group(loopGroup).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY, true)
					.handler(new ChannelInitializer<SocketChannel>() {
						@Override
						protected void initChannel(SocketChannel channel) throws Exception {
							channel.pipeline().addLast(new LengthPackageSpliter());
							channel.pipeline().addLast(new LenghtProtocoltDecode());
							channel.pipeline().addLast(new LengthProtocoltEncode());
							channel.pipeline().addLast(new MyClientHandler());
						}
					});
			// 启用客户端连接
			ChannelFuture future = b.connect(address, port).sync();
			future.channel().closeFuture().sync();
		} finally {
			loopGroup.shutdownGracefully();
		}
	}
	
	public static class MyClientHandler extends ChannelInboundHandlerAdapter {

		@Override
		public void channelActive(ChannelHandlerContext ctx) throws Exception {
			byte[] data = ("this is one line message.".getBytes());
			ByteBuf buff = Unpooled.buffer(data.length);
			buff.writeBytes(data);
			ctx.writeAndFlush(buff);
		}

		@Override
		public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
			ByteBuf buf = (ByteBuf)msg;
			byte[] readdata = new byte[buf.readableBytes()];
			buf.readBytes(readdata);
			System.out.println("client receive:" + new String(readdata));
		}
	}
	

}

 

3. Agregue codificación y decodificación de tipo String

En el caso anterior, usamos ByteBuf tanto para enviar como para recibir. Lo transformamos para usar el tipo String para enviar y el tipo String para recibir. Esto requiere el uso de dos codificadores, StringDecoder y StringEncoder, estas dos codificaciones.

¿Dónde se ubica el procesador en la cadena de procesamiento (canalización)?

En primer lugar, StringDecoder es una entrada de mensaje, que convierte ByteBuf en String, que debe colocarse entre el último procesador de tipo y el procesador personalizado, es decir, la tercera posición.

El StringEncoder debe recibir la entrada String del procesador personalizado y convertirla al tipo ByteBuf. Luego debe colocarse en una posición antes del procesador personalizado. La cadena total del procesador debe estar en el siguiente orden.

	ch.pipeline().addLast(new LengthPackageSpliter());
							ch.pipeline().addLast(new LenghtProtocoltDecode());
							ch.pipeline().addLast(new StringDecoder()); // 增加String解码
							ch.pipeline().addLast(new LengthProtocoltEncode());
							ch.pipeline().addLast(new StringEncoder()); // 增加String编码
							ch.pipeline().addLast(new MyClientHandler());

Luego, transformaremos correspondientemente los lugares donde se ingresan y leen los datos, de la siguiente manera:

public static class MyClientHandler extends ChannelInboundHandlerAdapter {

		@Override
		public void channelActive(ChannelHandlerContext ctx) throws Exception {
//			byte[] data = ("this is one line message.".getBytes());
//			ByteBuf buff = Unpooled.buffer(data.length);
//			buff.writeBytes(data);
//			ctx.writeAndFlush(buff);
			ctx.writeAndFlush("this is one line message");
		}

		@Override
		public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
//			ByteBuf buf = (ByteBuf)msg;
//			byte[] readdata = new byte[buf.readableBytes()];
//			buf.readBytes(readdata);
//			System.out.println("client receive:" + new String(readdata));
			System.out.println("client receive:" +msg );
		}
	}

La modificación del servidor es similar y se omite

 

 

¡¡¡fin!!!

 

 

 

 

 

Supongo que te gusta

Origin blog.csdn.net/shuixiou1/article/details/114901198
Recomendado
Clasificación