IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> Netty框架之TCP粘包/半包解决方案 -> 正文阅读

[网络协议]Netty框架之TCP粘包/半包解决方案


谈到TCP粘包/半包的解决方案,我们不妨先认识造成TCP粘包/半包的原因有哪些,以便于更深刻理解解决方案的原理

一.TCP粘包

1.现象:发送:abc,def,接收:abcdef
2.原因:

  • 应用层:接受数据的Bytebuf缓冲区设置太大(Netty默认为1024)存储了多个TCP包,一次性取的时候取出缓冲区所有数据,导致多个包粘在一起.
  • 滑动窗口:发送方发送消息太快,接收方接收消息也快,但是处理消息太慢,并且接收窗口足够大,那么此时消息就会堆积在接收窗口,那么就会导致多个包粘在一起
  • Nagle算法:Nagle算法是操作系统底层自动实现的,为了减少发送次数,提高效率,操作系统会在缓冲区未满的时候等待消息填充,直到缓冲区满的时候一并发送,造成粘包

二.TCP半包

1.现象:发送:abcdef,接收:abc,def
2.原因:

  • 应用层:接受数据的Bytebuf缓冲区设置太小(Netty默认为1024)存储不了一个完整的TCP包,一次性只能取出TCP包的部分数据
  • 滑动窗口:发送方发送窗口较大,接收方接收窗口较小,导致接收方只能接收TCP包的部分数据,导致TCP半包
  • MSS限制:为了限制TCP包的大小,会将大的TCP包分组成多个小的TCP包发送,导致半包问题

三.TCP粘包/半包解决方案

1.FixedLengthFrameDecoder定长解析器

向pipeline中加入FixedLengthFrameDecoder(int length)定长解析器,取出指定字节长度的数据作为完成数据,不过这要求完整数据的长度是定长

使用方法,请尝试理解下面源码

public class FixedLengthFrameDecoderTest {
    public static ByteBuf Message(){

        StringBuilder sb = new StringBuilder();
        char c = 'a';
        for(int i=0;i<20;i++){
            sb.append(c);
            c++;
        }
        return Unpooled.copiedBuffer(sb.toString(),StandardCharsets.UTF_8);
    }
    public static void main(String[] args) {
        EmbeddedChannel channel = new EmbeddedChannel(
                //定长解析器,取出指定字节长度的数据
                new FixedLengthFrameDecoder(10),
                new SimpleChannelInboundHandler<ByteBuf>() {
                    @Override
                    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf buf) throws Exception {
                        //将Bytebuf的信息读取到字节数组
                        int len = buf.readableBytes();
                        byte[] msg = new byte[len];
                        buf.readBytes(msg);
                        //把字节数组转化为String输出
                        System.out.println(new String(msg,0,len));
                    }
                }
        );
        //模拟入栈,向服务器发送消息
        channel.writeInbound(Message());
    }
}

2.LineBasedFrameDecoder行解析器

向pipeline中加入LineBasedFrameDecoder行解析器,会以换行符"\n"作为完整消息边界,不过着要求每一个完整消息的末尾都加上换行符作为

使用方法,请尝试理解下面源码

public class LineBasedFrameDecoderTes {
    //构造一个消息
    public static ByteBuf Massage(){
        StringBuilder msg = new StringBuilder();
        char c = 'a';
        for(int i=0;i<10;i++){
            msg.append(c);
            c++;
        }
        //加一个换行符,作为结束符
        msg.append("\n");

        //重复一次
        for(int i=0;i<10;i++){
            msg.append(c);
            c++;
        }
        //加一个换行符,作为结束符
        msg.append("\n");

        return Unpooled.copiedBuffer(msg.toString(), StandardCharsets.UTF_8);
    }
    public static void main(String[] args) {
        //网络通信测试类,可以模拟客户端向服务器发送消息,不用实现客户端和服务端,方便调试所用
        EmbeddedChannel channel = new EmbeddedChannel(
                //最大长度设置,如果到了1024还没有遇到结束符,那么会报错
                new LineBasedFrameDecoder(10),
                new SimpleChannelInboundHandler<ByteBuf>(){

                    @Override
                    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf buf) throws Exception {
                        //接收到消息,做输出处理
                        int len = buf.readableBytes();
                        byte[] msg = new byte[len];
                        //Bytebuf中的数据读取到byte字节数组
                        buf.readBytes(msg);

                        //byte字节数组转换为String字符串并输出
                        System.out.println(new String(msg,0,len));
                    }
                }
        );

        channel.writeInbound(Massage());

    }
}

3.LengthFieldBasedFrameDecoder

向pipeline中加入LineBasedFrameDecoder行解析器,以设置长度读取消息
,不过这要求数据以消息长度+消息体的形式发送,也即先告知服务器数据的长度,再发送完整的数据

参数设置:

1.maxFrameLength:消息最大字节长度
2.lengthFieldOffset:声明消息长度变量的偏移量(也即字节下标是多少)
3.lengthFieldLength:声明消息长度变量的字节大小(一般用int声明,4字节)
4.lengthAdjustment:调着消息体的其实位置,如果忽略一个字节就设置为1,不忽略设置为0
5.initialBytesToStrip:最终消息从头剥离几个字节

使用方法,请尝试理解下面源码

/**
 * LTC解码器要求:
 * 消息长度(int)+消息体一起发送
 */
public class LengFiledBasedFrameDecoderTest {
    public static ByteBuf Message(){
        ByteBuf buf = ByteBufAllocator.DEFAULT.buffer();
        StringBuilder sb = new StringBuilder();
        char c = 'a';
        for(int i=0;i<20;i++){
            sb.append(c);
            c++;
        }
        byte[] bytes = sb.toString().getBytes(StandardCharsets.UTF_8);
        buf.writeInt(bytes.length);
        buf.writeBytes(bytes);
        return buf;
    }
    public static void main(String[] args) {
        EmbeddedChannel channel = new EmbeddedChannel(
                /*参数设置
                1.maxFrameLength:消息最大字节长度
                2.lengthFieldOffset:声明消息长度变量的偏移量(也即字节下标是多少)
                3.lengthFieldLength:声明消息长度变量的字节大小(一般用int声明,4字节)
                4.lengthAdjustment:调着消息体的其实位置,如果忽略一个字节就设置为1,不忽略设置为0
                5.initialBytesToStrip:最终消息从头剥离几个字节
                *
                */
                new LengthFieldBasedFrameDecoder(1024,0,4,0,4),
                new SimpleChannelInboundHandler<ByteBuf>(){

                    @Override
                    protected void channelRead0(ChannelHandlerContext channelHandlerContext, ByteBuf buf) throws Exception {
                        int len = buf.readableBytes();
                        byte[] msg = new byte[len];
                        buf.readBytes(msg);
                        System.out.println(new String(msg,0,len));
                    }
                }
        );
        channel.writeInbound(Message());
    }

}
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-05-05 11:56:47  更:2022-05-05 11:57:20 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 2:41:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码