Netty usa um decodificador de comprimento para implementar o processamento de pacote pegajoso de meio pacote

I. Visão geral:

  1. Explicação do substantivo

  Meio pacote: protocolo TCP, existe um buffer, se um pacote enviado for maior que o tamanho do buffer, haverá um fenômeno de meio pacote, ou seja, um pacote é dividido em duas transmissões.

  Pacotes pegajosos: Também é devido à existência da área de buffer.Se muitos pacotes de pequeno porte forem enviados em um curto espaço de tempo, a camada inferior da implementação do protocolo TCP enviará esses pequenos pacotes juntos.

  2. Visão geral

  Em primeiro lugar, o aparecimento do pacote pegajoso de semi-pacote não é devido à estrutura da rede, mas um problema decorrente da implementação do protocolo TCP. Como resolver o pacote pegajoso e meio pacote, existem principalmente os seguintes métodos no framework netty que usamos.

  1) Defina o delimitador final do pacote

  2) O pacote de comprimento fixo é enviado e o pacote de comprimento fixo é decodificado para remover os espaços quando em uso

  3) Adicione uma marca na cabeça e remova a marca ao usá-la.

  3. Decodificadores integrados comuns

// (1) Use FixedLengthFrameDecoder para resolver o problema de pacotes pegajosos de mensagens de comprimento fixo;
//
// (2) Use LineBasedFrameDecoder e StringDecoder para resolver o problema de pacotes pegajosos TCP com retorno de carro e alimentação de linha como o final da mensagem ;
//
// (3) O decodificador de delimitador especial DelimiterBasedFrameDecoder é usado para resolver o problema do pacote fixo de TCP com símbolos especiais no final da mensagem; (é um método personalizado de 2)
//
// (4) O último um também é este artigo Use o decodificador de comprimento personalizado LengthFieldBasedFrameDecoder para resolver o problema do pacote fixo TCP.

 

Dois, codificação e decodificação

1. Processo de codificação e decodificação

O manipulador personalizado do cliente envia as informações, após o processamento de LengthProtocoltEncode (a posição é a penúltima), adiciona o nome do protocolo e o campo de comprimento e os envia para o servidor. O servidor passa pelo decodificador herdado de LengthFieldBasedFrameDecoder ( a posição é a primeira) Processamento automático, entregue ao decodificador de campo de comprimento LenghtProtocoltDecode processing (position is the second) e, finalmente, entregue ao último processador no lado do servidor para imprimir a entrada do cliente, e o Echo retorna os dados.

Nota: Se a posição do codificador for inserida, a entrada é processada da frente para trás. Portanto, após o comprimento ser decodificado, o comprimento é removido e a saída fica na direção oposta. Portanto, o penúltimo deve processar o conteúdo de saída real. (Além disso: se você adicionar um codificador de string, ele deve ser colocado atrás do processador personalizado, porque o processador personalizado grava dados e escreve String, que precisa ser processado pelo codificador de string)

2. Regras do acordo

  [Versão de protocolo de 2 comprimentos, campo de comprimentos de 2 comprimentos, conteúdo de pacote real]

3. Três processadores principais de codificação e decodificação

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);
		}
	}

 Três, implementação 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. Adicionar codificação e decodificação do tipo String

No caso acima, usamos ByteBuf para enviar e receber. Nós o transformamos para usar o tipo String para enviar e o tipo String para receber. Isso requer o uso de dois codificadores, StringDecoder e StringEncoder, essas duas codificações.

Onde o processador é colocado na cadeia de processadores (pipeline)?

Em primeiro lugar, StringDecoder é uma entrada de mensagem, que converte ByteBuf em String, que deve ser colocada entre o último processador do tipo e o processador customizado, ou seja, a terceira posição.

O StringEncoder deve receber a entrada String do processador personalizado e convertê-la no tipo ByteBuf. Em seguida, deve ser colocado em uma posição antes do processador personalizado. A cadeia total do processador deve estar na seguinte ordem.

	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());

Em seguida, vamos transformar correspondentemente os locais onde os dados são inseridos e lidos, da seguinte maneira:

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 );
		}
	}

A modificação do servidor é semelhante e omitida

 

 

fim!!!

 

 

 

 

 

Acho que você gosta

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