Netty(三、拆包粘包)

拆包粘包

TCP在向目标发送数据时,会通过缓冲区发送,当数据达不到缓冲区的大小时会发生粘包。当数据超过缓冲区大小时,则会分成多个部分,就是拆包。

 

 

 例如上图:我们实际发送和希望接收到这样的数据,但是实际却有可能接收成这样。

程序中,我们这样向服务器发送三条数据,而服务端只接收到一条:

 

 

 

 

 

 Netty解决方案

DelimiterBasedFrameDecoder

通过自定义分隔符实现拆包粘包

服务端:

//5.配发事件处理器流水线
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        String delimiter = "_$";
                        //定义拆包粘包的解码器
                        socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.wrappedBuffer(delimiter.getBytes())));
                        //解码器
                        socketChannel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
                        //编码器
                        socketChannel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
                        //自定义的编码器,输出信息后面加上分隔符
                        socketChannel.pipeline().addLast(new DelimiterBasedFrameEncoder(delimiter));
                        //自定义事件处理器
                        socketChannel.pipeline().addLast(new NettyServerHandler());


                    }
                });

自定义:DelimiterBasedFrameEncoder

package com.wk.test.nettyTest;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
@ChannelHandler.Sharable
public class DelimiterBasedFrameEncoder extends MessageToByteEncoder<String> {

    private String delimiter;

    public DelimiterBasedFrameEncoder(String delimiter){
        this.delimiter = delimiter;
    }

    @Override
    protected void encode(ChannelHandlerContext channelHandlerContext, String message, ByteBuf byteBuf) throws Exception {
        channelHandlerContext.writeAndFlush(Unpooled.wrappedBuffer((message + delimiter).getBytes()));
    }

}

客户端:

.handler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        String delimiter = "_$";
                        socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(1024, Unpooled.wrappedBuffer(delimiter.getBytes())));
                        //解码器
                        socketChannel.pipeline().addLast("decoder", new StringDecoder(CharsetUtil.UTF_8));
                        //编码器
                        socketChannel.pipeline().addLast("encoder", new StringEncoder(CharsetUtil.UTF_8));
                        socketChannel.pipeline().addLast(new DelimiterBasedFrameEncoder(delimiter));
                        socketChannel.pipeline().addLast(new NettyClientHandler());
                    }
                });

这里只列举这种方法,还有其他方法如固定解码长度等,这里不一一列举。

 

posted @ 2020-05-12 10:35  学霸王先森  阅读(220)  评论(0编辑  收藏  举报