TCP pegado y desembalaje

I. Resumen

        TCP está orientado a la conexión, al flujo y proporciona servicios altamente confiables. Tanto el extremo de recepción como el de envío (cliente y servidor) deben tener un par de sockets. Por lo tanto, para enviar varios paquetes al extremo de recepción de manera más eficaz, el remitente utiliza un método de optimización (algoritmo de Nagle). Combinar varios datos con intervalos pequeños y pequeño volumen de datos en un bloque de datos grande y luego empaquetar. Aunque esto mejora la eficiencia, es difícil para el extremo receptor distinguir el paquete de datos completo, porque la comunicación orientada al flujo no tiene un límite de protección de mensajes; dado que TCP no tiene un límite de protección de mensajes, el problema de los límites del mensaje debe tratarse en el extremo receptor, eso es lo que llamamos problemas de pegado y desempaquetado; al escribir el programa Netty, si no hay procesamiento, habrá problemas de pegado y desempaquetado.

2. Problema detallado

Tres, pegar y desempacar soluciones

  1. Utilice un protocolo personalizado + códec para resolver
  2. La clave es resolver el problema de la longitud de los datos que lee el servidor cada vez, si se resuelve este problema, el servidor no leerá más o menos datos, evitando así que se pegue y desempaquete TCP.

Cuatro, implementación de código

El cliente debe enviar 5 objetos de mensaje y el cliente envía un objeto de mensaje cada vez

Cada vez que el servidor recibe un mensaje, se descodifica en 5 veces y cada vez que se lee un mensaje, se devuelve un objeto de mensaje al cliente.

Cliente:

public class MyClient { 
    public static void main (String [] args) arroja Exception { 

        EventLoopGroup group = new NioEventLoopGroup (); 

        prueba { 

            Bootstrap bootstrap = new Bootstrap (); 
            bootstrap.group (grupo) .channel (NioSocketChannel.class) 
                    .handler (nuevo MyClientInitializer ()); // 自 定义 一个 初始化 类

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

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

        } finalmente { 
            grupo.shutdownGracefully (); 
        } 
    } 
}

 

public class MyClientHandler extiende SimpleChannelInboundHandler <MessageProtocol> { 

    private int count; 
    @Override 
    public void channelActive (ChannelHandlerContext ctx) throws Exception { 
        // Usa el cliente para enviar 10 datos "Hace frío hoy, come olla caliente" Número 

        para (int i = 0; i <5; i ++) { 
            String mes = "Hoy hace frío, come olla caliente"; 
            byte [] content = mes.getBytes (Charset.forName ("utf-8")); 
            int length = mes.getBytes (Charset.forName ("utf-8")). Length; 

            // Crear un objeto de paquete de protocolo 
            MessageProtocol messageProtocol = new MessageProtocol (); 
            messageProtocol.setLen (length);  
            messageProtocol.setContent (content);
            ctx.writeAndFlush (messageProtocol);

        } 

    }
 
// @Override 
    protected void channelRead0 (ChannelHandlerContext ctx, MessageProtocol msg) arroja Exception { 

        int len ​​= msg.getLen (); 
        byte [] contenido = msg.getContent (); 

        System.out.println ("客户 端接 收到 消息 如下"); 
        System.out.println ("长度 =" + len); 
        System.out.println ("内容 =" + nueva Cadena (contenido, Charset.forName ("utf-8"))); 

        System.out.println ("客户 端 接收 消息 数量 =" + (++ this.count)); 

    } 

    @Override 
    public void exceptionCaught (ChannelHandlerContext ctx, Throwable cause) arroja Exception { 
        System.out.println ("异常 消息 =" + cause.getMessage ());
        ctx.close (); 
    } 
}
La clase pública MyClientInitializer extiende ChannelInitializer <SocketChannel> { 
    @Override 
    protected void initChannel (SocketChannel ch) lanza Exception { 

        ChannelPipeline pipeline = ch.pipeline (); 
        pipeline.addLast (nuevo MyMessageEncoder ()); // 加入 编码 器
        pipeline.addLast (new MyMessageDecoder ()); // 加入 解码 器
        pipeline.addLast (new MyClientHandler ()); 
    } 
}

//Servidor

public class MyServer { 
    public static void main (String [] args) lanza Exception { 

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

        intente { 

            ServerBootstrap serverBootstrap = new ServerBootstrap (); 
            serverBootstrap.group (bossGroup, workerGroup) .channel (NioServerSocketChannel.class) .childHandler (nuevo MyServerInitializer ()); // 自 定义 一个 初始化 类


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

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

    } 
}
// El controlador que maneja la 
clase pública empresarial MyServerHandler extiende SimpleChannelInboundHandler <MessageProtocol> { 
    recuento de int privado; 

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

    @ Anular el 
    vacío protegido channelRead0 (ChannelHandlerContext ctx, MessageProtocol msg) throws Exception { 

        // Recibir los datos y procesarlos 
        int len ​​= msg.getLen (); 
        byte [] content = msg.getContent (); 

        System.out.println (); 
        System.out.println (); 
        System.out.println (); 
        System.out.println ("El servidor recibió la siguiente información");
        System.out.println ("length =" + len); 
        System.out.println ("content =" + new String (content, Charset.forName ("utf-8"))); 

        System.out.println (" El número de paquetes de mensajes recibidos por el servidor = "+ (++ this.count)); 

        // Mensaje de 

        respuesta String responseContent = UUID.randomUUID (). ToString (); 
        int responseLen = responseContent.getBytes (" utf-8 " ) .length; 
        byte [] responseContent2 = responseContent.getBytes ("utf-8"); 
        // Cree un paquete de protocolo 
        MessageProtocol messageProtocol = new MessageProtocol (); 
        messageProtocol.setLen (responseLen); 
        messageProtocol.setContent (responseContent2); 

        ctx. writeAndFlush (messageProtocol); 


    } 
}
La clase pública MyServerInitializer extiende ChannelInitializer <SocketChannel> { 

    @Override 
    protected void initChannel (SocketChannel ch) lanza Exception { 
        ChannelPipeline pipeline = ch.pipeline (); 

        pipeline.addLast (nuevo MyMessageDecoder ()); // 解码 器
        pipeline.addLast (nuevo MyMessageEncoder ()); // 编码 器
        pipeline.addLast (nuevo MyServerHandler ()); 
    } 
}

 

// 协议 包
public class MessageProtocol { 
    private int len; // 关键
    contenido de bytes privados []; 

    public int getLen () { 
        return len; 
    } 

    public void setLen (int len) { 
        this.len = len; 
    } 

    public byte [] getContent () { 
        devolver contenido; 
    } 

    public void setContent (byte [] contenido) { 
        this.content = contenido; 
    } 
}

// Códec

La clase pública MyMessageEncoder extiende MessageToByteEncoder <MessageProtocol> { 
    @Override 
    protected void encode (ChannelHandlerContext ctx, MessageProtocol msg, ByteBuf out) arroja Exception { 
        System.out.println ("MyMessageEncoder encode 方法 被 调用"); 
        out.writeInt (msg.getLen ()); 
        out.writeBytes (msg.getContent ()); 
    } 
}
public class MyMessageDecoder extiende ReplayingDecoder <Void> { 
    @Override 
    protected void decode (ChannelHandlerContext ctx, ByteBuf in, List <Object> out) throws Exception { 
        System.out.println ("Se llama a la decodificación de MyMessageDecoder"); 
        // Necesidad de obtener binario Bytecode -> MessageProtocol paquete de datos (objeto) 
        int length = in.readInt (); 

        byte [] content = new byte [length]; 
        in.readBytes (content); 

        // encapsular en el objeto MessageProtocol, ponerlo fuera y pasar Procesamiento de negocio del siguiente controlador 
        MessageProtocol messageProtocol = new MessageProtocol (); 
        messageProtocol.setLen (longitud); 
        messageProtocol.setContent (contenido); 

        out.add (messageProtocol); 

    } 
}

 

Supongo que te gusta

Origin blog.csdn.net/heijunwei/article/details/105621750
Recomendado
Clasificación