揭秘通信协议设计的奥妙,作为面试官我都看蒙了

2023-11-09 08:20

本文主要是介绍揭秘通信协议设计的奥妙,作为面试官我都看蒙了,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本节将重点探讨网络编程中一种非常通用的协议设计方法论:协议头 + 消息体。

所谓的通信协议就是通信双方共同遵循的一种“约定”,用于通信发送方将内容按照“通信协议”所规定的格式组装成**“二进制流”**,通信接收方按照“通信协议”所规定的格式正确的从二进制流中解码出一个个原始请求。

那通信协议如何设计呢?

1、通用的协议设计方法论

在网络编程中,流行着一种经典的协议设计方法论:协议头 + 消息体。

其设计的关键点如下:

协议头的长度是固定的,通常为识别一个业务的最小长度

协议头中会包含一个长度字段,用来标识一个完整包的长度,用来表示长度字段的字节位数直接决定了一个包的最大长度,长度字段通常被设计为4个字节。

消息体中存储业务数据,例如如果是一个Dubbo协议,那消息体中可能会包含请求参数、调用的服务名等,而且字符串类的存储通常会采取字段长度、字段内容的组织方式。

为了有一个更直观的展示,我以一个简单的RPC通信场景为例,实现类似Dubbo服务的远程服务调用,其通信协议可以简单设置成下图所示:

基于 Header + Boby 的通信协议设计模式后,通信接收方就能很好的从二进制流中非常容易地解码出一条一条原始的请求数据包,解码的基本套路如下(在面试中面试官非常喜欢问的“粘包”问题的破解之道)

首先判断累积缓存区中是否存在一个完整的Head头部,例如上述示例中,一个包的Header的长度为6个字节,那首先判断累积缓存中可读字节数是否大于等于6,如果不足6个字节,跳过本次处理,等待更多数据到达累积缓存区

尝试将头部6个字节读取,并且提取长度字段中存储的数值,即包长度,然后判断累积缓存区中可读字节数大于等于整个包的长度,如果累积缓存区不包含一个完整的数据包,则跳过本次处理,等待更多数据到达累积缓存区。

如果包含一个完整的包,则按照通信协议的格式按序读取相关的内容。

正是因为这种设计理念非常通用,Netty 对上述协议设计进行了统一封装:

LengthFieldBasedFrameDecoder 闪亮登场了,接下来我们来看看Netty是如何进行封装的,揭晓更多的实现细节,让大家做到理论与实践相结合。

2、LengthFieldBasedFrameDecoder 详解

2.1 概述

接下来对其核心属性进行一个详细的解读:

ByteOrder byteOrder

字节序列,Netty默认使用大端序列(主要是针对int、long等数值类型),所谓的大端序列,通常可以这样理解,接收端收到的字节流的顺序是从数值类型的高字节。

int maxFrameLength

一条消息最大的长度。

int lengthFieldOffset

代表长度字段的开始偏移量。

int lengthFieldLength

代表长度字段占用的字节长度。

int lengthFieldEndOffset

代表长度字段的结束偏移量,等于lengthFieldOffset + lengthFieldLength。

int lengthAdjustment

长度适配适配值。该值表示协议中长度字段与消息体字段直接的距离。

int initialBytesToStrip

跳过一个包中前面多少个字节不处理,通常是将协议头部跳过,只将消息体中内容传输到下游时使用

boolean failFast

是否快速失败。

boolean discardingTooLongFrame

是否吞没(跳过)大帧包。

long tooLongFrameLength

当前在处理吞没大包的实际大小。

long bytesToDiscard

下一次解码之前,需要先忽略的字节数,当遇到超过maxFrameLength的包时使用。

上面的属性如果不太好理解,没关系,因为本节的最后会有两张图勾画出协议的全貌(用图示的方式勾画出各个属性的位置与含义)

2.2 decode 方法详解

接下来我们来看一下其decode方法,通过阅读源码的方法来理解其内部的工作原理。

LengthFieldBasedFrameDecoder#decode

Step1:跳过无效数据包的处理逻辑。如果discardingTooLongFrame为true,表示正在处理大于****maxFrameLength的包,需要跳过这个超长的包,不对其解码,由于数据是陆续到达累积缓存区,并不能一次跳过整个无效包,故需引入 bytesToDiscard 变量,用于记录本次能跳过的字节,当 bytesToDiscard 为 0后表示一个无效包已全部跳过,需要处理正常数据包,此时discardingTooLongFrame 会重置为 false。

Step2:如果累积缓冲区的可读字节大小小于length字段的结束偏移量,返回null,结束解码,说明该累积缓存区中的数据还不完整。

Step3:尝试从累积缓存区中获取包的长度。其中表示 lengthFiedlOffset 表示长度字段的其实偏移量,在结合长度字段的长度 lengthFieldLength ,再结合字节序列**(大端序列、小端序列)**。

Step4:这里是包长度超过协议允许的最大包长度时的处理逻辑,在这里大家先姑且跳过 lengthAdjustment 属性的含义。

如果当前累积缓存区中的可读字节大于 frameLength,大于当前包的长度,可以通过调用 skipBytes 方法跳过这包。

如果当前累积缓存区的可读自己小于 frmaeLength,需要分多次跳过,故先将累积区中的数据全部跳过,然后通过 bytesToDiscard 记录还需要跳过的字节数。

Step5:如果累积缓存区中的数据不包含一个完整的包,返回null,结束本次解码,等待更多的数据包的到来。

Step6:通过 ByteBuf 的 slince 方法,提取一个完整的包长度,解码出完整的数据包,完成一个数据包解码。

2.3 图解 LengthFieldBasedFrame 协议

在Netty 的

LengthFieldBasedFrameDecoder 中有一个 lengthAdjustment 属性,可以是正数,也可以是负数,其使用的代码片段如下:

frameLength += lengthAdjustment + lengthFieldEndOffset

lengthAdjustment 长度调整字段,可以为正数,也可以为负数,主要的作用是 长度字段中的值是否包含 Header 长度本身,严格意义上来说应该是包含 长度字段之前的字节序列。

1、lengthAdjustment > 0

2、lengthAdjustment < 0

在大多数情况下,length字段表示消息正文的长度,但是有些协议,其长度表示的是整个消息的长度,故Netty为了适配这种情况,可以通过 lengthAdjustment 设置为负数,来调节数据帧的大小。

总结:lengthAdjustment 的出现是Netty为了适配现有的协议而设计出来的字段,即 Netty

LengthFieldBasedFrameDecoder 是为了i给 header + body ,并且基于长度字段的协议一种通用的解决方案,可以通过 lengthAdjustment 来准确表示数据帧(业务数据的长度),这里是一种逆向思维

3、协议设计子类的最佳实践

最佳实践:

LengthFieldBasedFrameDecoder 的 decode 方法的职责是从二进制流中解码出一个完整的数据包,其返回类型还是 ByteBuf,故自定义的编码解码器的 decode 方法就是先调用父类的 decode 方法 得到 ByteBuf ,然后对 ByteBuf 中的数据解码出对象。

LengthFieldBasedFrameDecoder 并不负责将 ByteBuf 转换为协议对象,而是从二进制流中解码出一个数据帧,而将ByteBuf 转换为协议对象的职责由其子类实现,通常的编码风格如下:

资源获取:
大家 点赞、收藏、关注、评论啦 、 查看 👇🏻 👇🏻 👇🏻 微信公众号获取联系方式 👇🏻 👇🏻 👇🏻
精彩专栏推荐订阅:下方专栏 👇🏻 👇🏻 👇🏻 👇🏻
每天学四小时:Java+Spring+JVM+分布式高并发,架构师指日可待

这篇关于揭秘通信协议设计的奥妙,作为面试官我都看蒙了的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/375011

相关文章

揭秘未来艺术:AI绘画工具全面介绍

📑前言 随着科技的飞速发展,人工智能(AI)已经逐渐渗透到我们生活的方方面面。在艺术创作领域,AI技术同样展现出了其独特的魅力。今天,我们就来一起探索这个神秘而引人入胜的领域,深入了解AI绘画工具的奥秘及其为艺术创作带来的革命性变革。 一、AI绘画工具的崛起 1.1 颠覆传统绘画模式 在过去,绘画是艺术家们通过手中的画笔,蘸取颜料,在画布上自由挥洒的创造性过程。然而,随着AI绘画工

在线装修管理系统的设计

管理员账户功能包括:系统首页,个人中心,管理员管理,装修队管理,用户管理,装修管理,基础数据管理,论坛管理 前台账户功能包括:系统首页,个人中心,公告信息,论坛,装修,装修队 开发系统:Windows 架构模式:B/S JDK版本:Java JDK1.8 开发工具:IDEA(推荐) 数据库版本: mysql5.7 数据库可视化工具: navicat 服务器:SpringBoot自带 ap

亮相WOT全球技术创新大会,揭秘火山引擎边缘容器技术在泛CDN场景的应用与实践

2024年6月21日-22日,51CTO“WOT全球技术创新大会2024”在北京举办。火山引擎边缘计算架构师李志明受邀参与,以“边缘容器技术在泛CDN场景的应用和实践”为主题,与多位行业资深专家,共同探讨泛CDN行业技术架构以及云原生与边缘计算的发展和展望。 火山引擎边缘计算架构师李志明表示:为更好地解决传统泛CDN类业务运行中的问题,火山引擎边缘容器团队参考行业做法,结合实践经验,打造火山

DDei在线设计器-API-DDeiSheet

DDeiSheet   DDeiSheet是代表一个页签,一个页签含有一个DDeiStage用于显示图形。   DDeiSheet实例包含了一个页签的所有数据,在获取后可以通过它访问其他内容。DDeiFile中的sheets属性记录了当前文件的页签列表。   一个DDeiFile实例至少包含一个DDeiSheet实例。   本篇最后提供的示例可以在DDei文档直接预览 属性 属性名说明数

基于Springboot + vue 的抗疫物质管理系统的设计与实现

目录 📚 前言 📑摘要 📑系统流程 📚 系统架构设计 📚 数据库设计 📚 系统功能的具体实现    💬 系统登录注册 系统登录 登录界面   用户添加  💬 抗疫列表展示模块     区域信息管理 添加物资详情 抗疫物资列表展示 抗疫物资申请 抗疫物资审核 ✒️ 源码实现 💖 源码获取 😁 联系方式 📚 前言 📑博客主页:

比较学习难度:Adobe Illustrator、Photoshop和新兴在线设计平台

从入门设计开始,几乎没有人不知道 Adobe 公司两大设计软件:Adobe Illustrator和 Photoshop。虽然AI和PS很有名,有一定设计经验的设计师可以在早期探索和使用后大致了解AI和PS的区别,但似乎很少有人会系统地比较AI和PS。目前,设计软件功能多样,轻量级和网页设计软件已成为许多设计师的需求。对于初学者来说,一篇有针对性的AI和PS比较总结文章具有非常重要的指导意义。毕竟

基于Java医院药品交易系统详细设计和实现(源码+LW+调试文档+讲解等)

💗博主介绍:✌全网粉丝10W+,CSDN作者、博客专家、全栈领域优质创作者,博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌💗 🌟文末获取源码+数据库🌟 感兴趣的可以先收藏起来,还有大家在毕设选题,项目以及论文编写等相关问题都可以给我留言咨询,希望帮助更多的人  Java精品实战案例《600套》 2023-2025年最值得选择的Java毕业设计选题大全:1000个热

展厅设计主要的六大要素

1、从创意开始      展示设计的开始必须创意在先。根据整体的风格思路进行创意,首先要考虑的是主体的造型、大小高度位置以及它和周围展厅的关系。另外其他道具设计制作与运作方式也必须在创意中有明确的体现。      2、平面感      平面感是指对展示艺术设计平面图纸审美和功能两个方面理性的感觉认识。它是三维空间设计认识的基础,也是施工的重要依据。展示空间的设计应先在展场环境的平面

华为某员工爆料:偷偷跑出去面试,被面试官鄙视了。第一句话就问:华为淘汰的吧,35岁了,这个年龄在华为能混得下去吗?身体没啥毛病吧

“你都35岁了,难不成是被华为淘汰的?在华为混不下去了吧?身体没啥毛病吧,我们这体检可是很严的。” 近日,一位华为员工在朋友圈爆料,自己在面试时遭到了面试官的无理取闹和人身攻击,原因仅仅是因为他35岁了,曾经在华为工作过。 这番话,充满了傲慢与偏见,让人听了义愤填膺。这位面试官的言行,不仅是对求职者的不尊重,更是对职场规则的践踏。 面试本应是双向选择的过程,企业和求职者在相互了解的基

办理河南建筑工程乙级设计资质的流程与要点

办理河南建筑工程乙级设计资质的流程与要点 办理河南建筑工程乙级设计资质的流程与要点主要包括以下几个方面: 流程: 工商注册与资质规划:确保企业具有独立法人资格,完成工商注册,并明确乙级设计资质的具体要求,包括注册资本、人员配置、技术条件等。 专业技术人员配置: 雇佣或签约符合资质要求的专业技术人员,包括但不限于:一级注册结构工程师2名、一级注册建筑师2名、注册暖通工程师1名、注册供配电工