美文网首页
(二)使用netty自带的编码器,来解决拆包,粘包的问题

(二)使用netty自带的编码器,来解决拆包,粘包的问题

作者: guessguess | 来源:发表于2021-02-19 15:35 被阅读0次

拆包,粘包。
其实就是我们对通道进行读写数据的时候,tcp协议因为各种原因,对数据的分发次数进行控制,可能多个数据包整合成一个数据,一次过发送,也可能是一个数据包,由于过大,被拆成几份,进行分发。
下面写一个例子验证一下。

需要用到的demo,在这个demo的基础上进行调整。

对客户端处理器适配器的调整

其实只是添加了俩个字段,用于统计客户端发送多少条信息,服务端给客户端返回多少信息。

package client.handler.adapter;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class TimeClientChannelHandlerAdapter extends ChannelInboundHandlerAdapter{
    private int count;
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        String req = "what time it is";
        ctx.writeAndFlush(Unpooled.copiedBuffer(req.getBytes()));
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf req = (ByteBuf) msg;
        byte[] reqinfo = new byte[req.readableBytes()];
        req.readBytes(reqinfo);
        System.out.println("now is " + new String(reqinfo, "UTF-8"));
        System.out.println("客户端接收到第" + ++count + "条信息");

    }
}

对于服务端处理器适配器的调整

package server.handler.adapter;

import org.joda.time.DateTime;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class TimeServerChannelHandlerAdapter extends ChannelInboundHandlerAdapter{
    private int count;
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf req = (ByteBuf)msg;
        byte[] reqInfo = new byte[req.readableBytes()];
        req.readBytes(reqInfo);
        System.out.println(new String(reqInfo, "UTF-8"));
        System.out.println("服务端接收到第" + ++count + "条信息");
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        ctx.writeAndFlush(Unpooled.copiedBuffer(DateTime.now().toString().getBytes()));
    }
}
服务端运行结果
客户端运行结果

从上述运行结果中看出,目前是没有什么问题的。接下来,进行调整。客户端改为遍历发送消息。

package client.handler.adapter;

import java.util.stream.Stream;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class TimeClientChannelHandlerAdapter extends ChannelInboundHandlerAdapter{
    private int count;
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Stream.iterate(0, i -> i + 1).limit(100).forEach(i -> {
            String req = "what time it is";
            ctx.writeAndFlush(Unpooled.copiedBuffer(req.getBytes()));
        });
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ByteBuf req = (ByteBuf) msg;
        byte[] reqinfo = new byte[req.readableBytes()];
        req.readBytes(reqinfo);
        System.out.println("now is " + new String(reqinfo, "UTF-8"));
        System.out.println("客户端接收到第" + ++count + "条信息");

    }
}
服务端运行结果
客户端运行结果

从上面俩个图看出,客户端将100个消息,压缩成2个消息进行发送,但是服务端也并没有返回2个响应,而是只响应了一次。
这里面就涉及到我们说的粘包和拆包。客户端将100个消息合并成2个数据包,分别发送,而服务端,也是将返回结果进行了粘包,只发送了一次。

那么要如何解决问题?
netty本身已经提供了组件给我们使用
如LineBasedFrameDecoder(根据换行符作为分割符)
如DelimiterBasedFrameDecoder (可以指定特定的符号作为分割符)
如StringDecoder

下面利用这些组件做调整,服务端和客户端按如下进行调整即可。

package client.handler.adapter;

import java.util.stream.Stream;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class TimeClientChannelHandlerAdapter extends ChannelInboundHandlerAdapter{
    private int count;
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Stream.iterate(0, i -> i + 1).limit(100).forEach(i -> {
            String req = "what time it is" + System.getProperty("line.separator");
            ctx.writeAndFlush(Unpooled.copiedBuffer(req.getBytes()));
        });
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        String info = (String)msg;
        System.out.println("now is " + info);
        System.out.println("客户端接收到第" + ++count + "条信息");
    }
}



package server.handler.adapter;

import org.joda.time.DateTime;

import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

public class TimeServerChannelHandlerAdapter extends ChannelInboundHandlerAdapter{
    private int count;
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        System.out.println((String)msg);
        System.out.println("服务端接收到第" + ++count + "条信息");
        String msgInfo = DateTime.now().toString() + System.getProperty("line.separator");
        ctx.writeAndFlush(Unpooled.copiedBuffer(msgInfo.getBytes()));
    }
}

运行结果如下,可以看出粘包拆包的问题已经被解决了。


服务端运行结果
客户端运行结果

这里面要注意的点。
1.读取消息的时候不能跟之前一样直接使用ByteBuf进行强转,需要使用String类型,因为是使用StringDecoder最后解码,加入输出结果的是利用toString方法生成的String对象。

相关文章

网友评论

      本文标题:(二)使用netty自带的编码器,来解决拆包,粘包的问题

      本文链接:https://www.haomeiwen.com/subject/mvugxltx.html