| |
|
开发:
C++知识库
Java知识库
JavaScript
Python
PHP知识库
人工智能
区块链
大数据
移动开发
嵌入式
开发工具
数据结构与算法
开发测试
游戏开发
网络协议
系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程 数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁 |
-> 网络协议 -> Netty(七) Netty编解码与TCP粘包拆包 -> 正文阅读 |
|
[网络协议]Netty(七) Netty编解码与TCP粘包拆包 |
? ?在我们了解了netty的基本组件之后,我们来了解netty一下使用netty带来的问题以及如何解决. ?1.Netty编解码1.1? Netty涉及到编解码的组件有Channel、ChannelHandler、ChannelPipe,我们再来复习一下 一个 Channel 包含了一个 ChannelPipeline,而 ChannelPipeline 中又维护了一个由 ChannelHandlerContext 组成的双向链表,并且每个 ChannelHandlerContext 中又关联着一个 ChannelHandler,我们业务逻辑写在ChannelHandler里面. read事件(入站事件)和write事件(出站事件)在一个双向链表中,入站事件会从链表 head 往后传递到最后一个入站的 handler,出站事件会从链表 tail 往前传递到最前一个出站的 handler,两种类型的 handler 互不干扰. 1.2? 为什么我们需要编解码器? ? ? ? ?首先接单介绍一下,序列化和反序列化大概念,在网络传输通信中,会发生两种数据转换的操作,一种是把消息对象转换成字节码,这种是序列化,还有一种是要把字节码对象再转换成消息对象,称为反序列化.和netty的对应关系,序列化对应的是编码过程,反序列化对应的解码过程.业界里面也有其他编码框架: google的 protobuf(PB)、Facebook的Trift、Jboss的Marshalling、Kyro等. ? ? ? 个人理解就是网络中传输都得是二进制0,1,所以我们写到网络中需要把我的字符串或者对象,转变为字节码,这叫编码.然后从网络中把字节码读取出来再解码成我们对应的字符串或者对象. ? ? ? 如果要实现高效的编解码可以用protobuf,但是protobuf需要维护大量的proto文件比较麻烦,现在一般可以使用protostuff。protostuff是一个基于protobuf实现的序列化方法,它较于protobuf最明显的好处是,在几乎不损耗性能的情况下做到了不用我们写.proto文件来实现序列化。使用它也非常简单,这里不做过多介绍. 1.3??为啥jdk有编解码,还要netty自己开发编解码? ? ? ? 1)无法跨语言 ? ? ? 2) 序列化后的码流太大,也就是数据包太大 ? ? ? 3) 序列化和反序列化性能比较差 1.4? Netty里面的编解码: ? ??解码器:负责处理“入站 InboundHandler”数据 ? ? 编码器:负责“出站 OutboundHandler” 数据 ? ? Netty里面提供默认的编解码器,也支持自定义编解码器 ? ? ? ? ?Encoder:编码器 ? ? ? ? ?Decoder:解码器 1.5??Netty的解码器Decoder和使用场景
1.6??Netty编码器Encoder
1.7? 代码演示 下面我们加的 1.StringDecoder为解码器(其实就是入站Handler,继承了ChannelInboundHandler),当我的服务端有消息流入的时候会经过我们入站Handler,这个Handler就是为了把字节码转换为我们的String,并将已经解码的字节转发给ChannelPipeline中的下一个ChannelInboundHandler. 2.StringEncoder为编码器(其实就是出站Handler,继承了ChannelOutboundHandler),当我们服务端写出消息给客户端的时候会经过我们的出站Handler,将我们的字符串编码为字节码. 3.ChatServerHandler,我们自定义的入站Handler,StringDecoder把字节码处理完就给到我们的这个Handler去处理业务.
2.TCP粘包,拆包? ? ?我们常常说TCP粘包,拆包,其实这个概念是伪科学. 2.1演示 ? 首先客户端发送100000次"你好,世界,我是Netty_",服务端仅仅是输出message
下面是我们服务端获取到的结果: ? ? ? ?我们可以发现,服务端获取到的结果红标4是两条'你好,世界,我是Netty_'合并在一起,是因为发生了粘包,而上面第2行世界的'界'乱码了是因为UTF-8的中文是3个字节,因为发生了拆包,3个字节被拆分了两部分,所以我们看到红标2的结尾和红标3的开头是乱码.不仅如此,红标2也发生了粘包问题. ?? 2.2粘包拆包的原因,以及为什么他是伪科学 ? ? ? TCP是一个流协议,就是没有界限的一长串二进制数据。TCP作为传输层协议并不不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行数据包的划分,所以在业务上认为是一个完整的包,可能会被TCP拆分成多个包进行发送,也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。面向流的通信是无消息保护边界的。 1)TCP拆包: 一个完整的包可能会被TCP拆分为多个包进行发送 2)TCP粘包: 把多个小的包封装成一个大的数据包发送, client发送的若干数据包 Server接收时粘成一包 ? ? 发送方和接收方都可能出现这个原因 ? ? ? ? 发送方的原因:TCP是流协议,TCP默认会使用Nagle算法?(Nagle算法就是为了尽可能发送大块数据,避免网络中充斥着许多小数据块,当你连续发送小数据块的时候,他会收集到一定的大小才发出去) 接收方的原因: TCP接收到数据放置缓存中,应用程序从缓存中读取? 3)所以这并不是TCP的锅,TCP只需要完整和有顺序的把要发的数据发给服务端就好了,你服务端怎么接收也不关我的事情,TCP粘包拆包说得像TCP的BUG一样. 4)UDP: 是没有粘包和拆包的问题,有边界协议 如下图所示,client发了两个数据包D1和D2,但是server端可能会收到如下几种情况的数据。 2.3 如何解决粘包,拆包问题(也叫半包读写) ? ? ? 解决粘包和拆包问题,得回到我们服务端,因为只有我们上层的服务端才准确知道发送数据的含义,以及如何分割包 解决方案 : 1)消息定长度,传输的数据大小固定长度,例如每段的长度固定为100字节,如果不够空位补空格 2)在数据包尾部添加特殊分隔符,比如下划线,中划线等,这种方法简单易行,但选择分隔符的时候一定要注意每条数据的内部一定不能出现分隔符。 3)发送长度:发送每条数据的时候,将数据的长度一并发送,比如可以选择每条数据的前4位是数据的长度,应用层处理时可以根据长度来判断每条数据的开始和结束。 Netty提供了多个解码器,可以进行分包的操作,如下: LineBasedFrameDecoder (回车换行分包) DelimiterBasedFrameDecoder(特殊分隔符分包) FixedLengthFrameDecoder(固定长度报文来分包) 这些分包解码器只需要放在我们的字符串解码器之前就好了,这些分包解码器也是入站Handler,继承了我们的InboundHandler ? DelimiterBasedFrameDecoder解决TCP半包读写问题 构造函数 public DelimiterBasedFrameDecoder( int maxFrameLength, boolean stripDelimiter, boolean failFast, ByteBuf delimiter)
使用方式: 下面代码增加了DelimiterBasedFrameDecoder,特殊分隔符,用下划线'_'分割每一条消息
2.4?自定义解码器 ? ? ?可以看到我们自定义了一个协议包,包含两个属性,第一个是len代表消息体长度,第二个是contetn代码内容,我们的解码器每一次读4个字节,如果是4个字节就继续往下的逻辑,不够4个字节,就return,为什么是4个字节?因为我们int就是4个字节,代表了消息体长度.
2.5? 官方自定义长度半包读写器LengthFieldBasedFrameDecoder 这个解码器也是根据消息的?Header+Body来拆分,即我的消息前多少位代表长度,剩下的是我真正的消息 使用方法也和之前使用DelimiterBasedFrameDecoder一样,就不多说,下面介绍一下他的构造函数的参数. 官方文档:https://netty.io/4.0/api/io/netty/handler/codec/LengthFieldBasedFrameDecoder.html 构造函数 public LengthFieldBasedFrameDecoder( int maxFrameLength, int lengthFieldOffset, int lengthFieldLength, int lengthAdjustment, int initialBytesToStrip)
|
|
网络协议 最新文章 |
使用Easyswoole 搭建简单的Websoket服务 |
常见的数据通信方式有哪些? |
Openssl 1024bit RSA算法---公私钥获取和处 |
HTTPS协议的密钥交换流程 |
《小白WEB安全入门》03. 漏洞篇 |
HttpRunner4.x 安装与使用 |
2021-07-04 |
手写RPC学习笔记 |
K8S高可用版本部署 |
mySQL计算IP地址范围 |
|
上一篇文章 下一篇文章 查看所有文章 |
|
开发:
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 10:25:26- |
|
网站联系: qq:121756557 email:121756557@qq.com IT数码 |