Netty进阶之粘包与拆包
TCP传输协议的源译特性及Nagle算法导致的粘包与拆包问题
在面向连接的TCP通信中,数据是码编以字节流形式传输。Nagle算法通过合并多次发送的源译小数据包,提高网络传输效率。码编然而,源译这种无消息保护边界的码编特性导致接收端难以准确识别数据包。粘包和拆包问题正是源译基于此原理,数据包合并或部分数据包丢失,码编影响数据传输的源译完整性和一致性。
图解粘包和拆包现象,正常消息发送与异常情况的对比。
程序演示部分,通过示例代码展示客户端连续发送消息,服务端接收并打印结果的过程。粘包问题随机性导致不同运行结果的差异。
解决方案分为三种:
1. 使用LineBasedFrameDecoder,通过在数据末尾添加特殊分隔符(默认为换行符)来标识数据包边界,简化问题解决。客户端需使用LineEncoder编码器,服务端使用LineBasedFrameDecoder解码器。发送消息时,在末尾添加分隔符。
2. 实施自定义长度帧解码器,定义数据长度,按长度获取数据。构造器包含多个参数,包括长度域设置、矫正偏移量、丢弃起始字节数等。此方法相对复杂,但能有效解决粘包问题。
3. 利用Google Protobuf编解码器,提供语言无关、平台无关的数据序列化机制。支持结构化数据的php 8.3源码编译安装高效存储与传输。通过编译.proto文件生成代码,客户端添加编码器,服务端添加解码器,解决粘包问题。
使用Google Protobuf编解码器解决粘包问题的步骤:
- 下载编译器,生成代码文件。
- 编写.proto文件,定义数据结构。
- 使用编译器生成Java代码。
- 客户端添加编码器,服务端添加解码器。
- 发送和接收消息,验证问题解决。
通过使用ProtobufVarintLengthFieldPrepender编码器和ProtobufVarintFrameDecoder解码器,可以解决Protocol编解码器存在的粘包问题。
总结,解决TCP粘包与拆包问题需根据具体需求和场景选择合适的方法,使用分隔符、自定义长度帧或Google Protobuf编解码器都是有效的策略。实践步骤包括配置编码器与解码器、编写相关代码以及验证解决方案。
内容总结及鼓励部分,强调了文章的创作不易,鼓励读者点赞并关注公众号。强调持续学习和交流的重要性。
一个SDK给我干懵逼了?大厂的SDK就这? Netty 版本的跃迁史
在日常开发中,我遇到过一件让我有些困惑的事情。那天,我在专注地编写 Bug 的时候,一位同事突然来找我,带来了一个非常特别的三方依赖库的 jar 包。这个 jar 包里包含了一些 Netty 的依赖,但问题是:无法确定具体是哪个版本的 Netty。我被这个“惊喜”搞得有点懵。
于是,我接过同事递过来的 jar 包,首先对它进行了解压。这个 jar 包的目录结构看起来与我所熟悉的某宝、某钉的 SDK 并不相同,没有常规的获客系统源码全部 pom 文件或 gradle 文件。我感到有些不解,这些信息通常会明确指出依赖库的版本,但在这里却找不到踪迹。
我开始怀疑,这可能是个不按套路出牌的黑科技。我反复检查了这个 jar 包的目录,却始终找不到依赖库的坐标声明文件。这时,同事催促着要我帮忙解决问题,我只好暂时放下这个疑问,先试着通过版本试用的方法来确定这个 jar 包中 Netty 的具体版本。
在查看这个 jar 包中的文件时,我发现其中包含了大量的 org.jboss.netty 依赖。我决定通过 mvnrepository.com 这个网站来搜索相关信息。输入 netty 关键字后,我发现搜索结果的前面大多数是 io.netty 的信息,直到第 7 个才出现了 org.jboss.netty 的信息。我进一步点击进入,发现提供的版本主要集中在 Netty3.0.x、3.1.x、3.2.x 系列。
根据常识,项目中引用 Netty 通常都会选择最终稳定版本,因此我尝试在 jar 包的源文件中添加了一个 pom 文件,并使用 3.2..Final 这个版本进行测试。然而,在编译源代码时,我发现缺少了 org.jboss.netty.handler.codec..xeblog.plugin.client.XEChatClient,执行“Tasks > build > assemble”来完成idea插件打包。
安装步骤为:“IDEA > Preferences > Plugins”,选择已打包的文件“build/distributions/xechat-plugin-xxx.zip”进行本地安装。若条件允许,建议自行部署服务端。
安装体验包括添加插件库、设置按钮和Manage Plugin Repositories...等操作,搜索“xechat”即可完成插件安装。此插件提供即时聊天、idea摸鱼工具和idea斗地主等实用功能,满足IDEA用户多样化需求。
我的提前金叉 指标源码世界安全插件初始化,老是进不去,而且初始化也一直不成功,怎么办?
我自己不写Java,也没看过Minecraft的源码,但看过许多mod作者的吐槽,列举如下:
完全没有API的概念。所有mod都是解包后强行hack进源码,甚至要用到一堆private方法。forge/bukkit是把这个hack工作进行封装抽象,模拟了一组API。
几乎没有事件概念,连它自己用的Java网络框架(netty)的事件架构都没用上,mod要自己做个游戏内的交互界面,得自己从头到尾造轮子,包括处理鼠标事件。
完全没有多线程。有人作了全文搜索,代码里仅有的几个thread和synchronized只出现在I/O相关的地方。直到最新的几个版本更新后,才开始为区块生成做了多线程,区块/单位更新这种最急需的不知道最新版本是不是多线程了。
因为没有多线程,Minecraft的服务器是市面上极其稀有的,用酷睿比用至强性能好的,因为酷睿单核主频比志强高……然而哪怕是顶配服务器,让人同时在线就能给你卡飞……心疼所有腐竹的钱包,有这钱租服务器,搭个魔兽私服都舒服得多。
对 OpenGL 的应用,连入门级别都算不上,OpenGL 中 glVertexBuffer 根本没使用上,导致图形性能奇差无比,于是出现了装上 OptiFine这个光影 mod 后反而帧数更高的诡异结果……因为这个游戏太流行了,几乎所有安卓系统的厂商,都不得不在 GPU 驱动里,专门为手机 Minecraft 开了个绿色通道,在 Minecraft 调用 OpenGL 接口时,系统帮忙擦屁股,把 glVertexBuffer 补上……(详见/answer/)
同上,方块渲染绘制时,表白墙的推荐源码根本没管方块是否可见,只要是所有暴露出来的面都会被绘制……比如区块加载未完成时能看到的地底方块……比如某些明明一马平川却能把人卡飞的地方,地底肯定有一个大型矿洞。
再同上,单线程+无优化,导致了Minecraft里万恶的区块更新策略的诞生(只有玩家角色附近的区块会更新,其他区块都是时间静止的),严重影响了机械化/工业化/红石化玩家的经营规模。
方块列表是一个数组,静态长度数组。没有方块注册接口,程序初始化时直接array[0] = new xxx(1, 3, "Dirt", ...); array[1] = new xxx(xx, xx, ...)这样把所有属性硬编码进去,如此写了几百行初始化上所有方块类型。方块的唯一标识就是数组下标,导致不同mod添加的自定义方块极容易因为下标重复而撞车。(比如某两个大型工业mod(看其他回答,这个feature终于被当作bug修复掉了?
因为根本没有API设计,所以方块的实现非常困难。实现一个新方块,需要继承自方块的基类并填充接口方法,那个基类貌似有+的接口方法……
因为上面的各个问题,导致Minecraft貌似至今都无法让一个格子里放下任意两种不同半砖的组合,因为每种组合都必须硬编码一个新方块来实现,而无法通过一个“半砖组合机制”来自动完成——建筑玩家体验极差。
药水同样是一个静态数组,同样的硬编码初始化,更惨的是,药水数组长度只有,一开始就写死了。听说有的主打药水的mod,是自己从零开始硬造了另一套药水系统,来绕过这个限制的。(这个操作似曾相识……前面说的写GUI/HUD的mod也是这么搞的)
因为没有API,不同版本之间的代码变化非常大。并且作者还头铁(或者技术不够),不肯(或者不会)二进制发布,发布的是Java包,极容易解包源码。所以作者还做了代码混淆,然而不同版本之间的代码混淆方式也不同,导致代码变动更大了——因此,Minecraft的mod更新极难无比,大多数大中型mod都是直接绑定在少数几个常用版本里的,而没法全版本适配或者迅速跟进新版本——因为每次更新后,forge/bukkit作者都得重新解包原码,重新反混淆,然后重新适配API,更新mod框架。
上一条的直接后果是,Minecraft的所有mod作者(包括不写mod的高清贴图作者,因为高清贴图依赖于mod),都是hack了游戏本身,违反了游戏软件的用户许可协议EULA。然而Mojang默许了这些行为的存在,并不去管——因为自己没本事做出稳定的API和mod框架,管了的话mod社区就不存在了,游戏就火不起来了。
某个评论都不敢开的答主居然说我们看到的代码烂因为它是反编译的,不是开发者写的源码,而且说有遮挡判断所以 drawCall 很低,性能很好?笑掉大牙了,编译优化会把一堆设计良好的类结构组成的方块系统合并成有几千行代码两百多个 API 的基类?编译优化会把方块/药水 ID 注册机制改造成静态数组?编译优化会把半砖拼接系统运行时代码干掉直接变成硬编码?编译优化会把 glVertrxBuffer 删除然后等驱动给你打补丁?编译优化会把多线程砍没?编译优化会把 netty 的事件层调用完全砍掉,让反编译呈现出没有事件系统的表现?任何一个合格的遮挡判断会在地表完全看不见时依然绘制矿洞?Minecraft 的编码水平之烂已经不是反编译能洗的了。
微软收购Mojang后,用C++基于移动版将其重写了,放到了win应用商城里。有Java正版key的用户可以到Minecraft官网上激活win应用商店版。有兴趣的可以对比一下,在win版和Java版上都开一个默认存档,进入世界,简单移动鼠标转换下视角,你就会感受到天壤之别。(话说网易和微软做了PY交易,把MC盒子包装了下美其名曰国服版,然后win应用商店版在中文区下架了……之前购买/激活了的老用户还可以用,没购买/激活的新用户,只能改系统语言跨区购买了)
再来个很残忍的对比——Minecraft之后,市面上兴起的类似设计的沙盘类游戏,除了泰拉瑞亚还是像素风,其他全特么是真·3D,而不是马赛克世界。
听说巨硬接手后1.8开始,许多问题大有改观,微软爸爸赛高!
综上所述,每一个mod作者上辈子都是折翼的天使,这辈子都是走向烈士之路的壮士
Springboot 整合 Netty 实战
本文介绍如何使用SpringBoot整合Netty,详细步骤如下:
首先,将Netty服务端和客户端置于同一个SpringBoot工程中,可通过在指定方法上使用@PostConstruct注解启动NettyServer类。
然后,构建Netty客户端,代码与服务端类似,客户端需要包含断线重连逻辑。
使用protobuf构建通信协议,它是一种高效轻量级的数据存储格式,适用于数据交换和存储。
protobuf提供跨语言支持,消息编码后体积小,性能高,广泛应用在各种项目中。
在Java中使用protobuf主要分为定义消息格式、使用.proto文件编译器生成Java类、引入protobuf-java依赖并使用生成的类。
Netty支持protobuf提供专门的编解码器,如ProtobufDecoder、ProtobufEncoder等。
心跳机制在TCP长连接中非常重要,实现方式有TCP层面的keepalive机制和自定义心跳数据包。Netty提供了IdleStateHandler来实现心跳机制。
客户端需实现心跳机制,当连接空闲时触发IdleStateEvent事件,处理该事件以发送心跳数据包。
Netty客户端实现断线重连,通过监听连接状态和在数据读写Handler中处理ChannelInactive事件来实现。
服务端空闲检测通过IdleStateHandler完成,检测一段时间内是否有数据读写,没有则及时释放资源。
创建一个Controller方法测试SpringBoot整合Netty的通信,通过调用NettyClient发送消息。
总结,使用SpringBoot整合Netty涉及构建服务端和客户端、使用protobuf通信协议、实现心跳机制、客户端断线重连以及服务端空闲检测。
Win下Jenkins-2.源码编译及填坑笔记
安装JDK与配置环境
首先安装JDK版本1.8-,确保操作系统中已添加JDK环境变量。通过执行"Java -version"命令验证JDK安装。注意,JDK版本必须在1.8.0-以上,Jenkins 2.版本不支持Java9,Maven版本需在3.5.3以上。
设置Maven环境与仓库路径
解压Maven3.5.4至指定英文路径,并添加Maven环境变量。配置Maven的conf\setting.xml文件,定位到行,设置本地Maven仓库路径为"C:\jstao\soft\sprintbootjar\repository"。定位到行,配置远端阿里云仓库,以方便访问相关资源。
解压Jenkins源码
解压Jenkins-2.源码至英文路径下。注意,解压前需确保目标目录为空。
源码编译与打包
以管理员身份运行CMD,进入Jenkins解压目录。执行命令"mvn validate"进行项目校验,首次执行可能需等待一段时间。接着执行"mvn clean install -Dmaven.test.skip=true"跳过单元测试编译项目,首次编译亦需等待。校验和编译过程完成后,可在war\target目录下找到GeoDevOps.war文件。
启动与测试
运行GeoDevOps.war文件,执行命令"java -jar GeoDevOps.war",访问http://localhost:进行测试。确认编译打包过程顺利。
源码编译踩坑记录
使用GitHub最新版本Jenkins源码编译时,因依赖包问题而失败,后切换至稳定版本Jenkins-2.。发现JDK版本需在1.8.0-以上,以避免校验失败。编译过程中,可能存在包依赖无法解决的问题,通过手动下载缺失jar包和对应POM文件至本地仓库,可有效解决冲突。
环境配置不当引发的问题
使用本地Maven版本3.3.9编译时,编译失败,原因是Maven版本需在3.5.3以上以兼容Jenkins-2.版本需求。执行编译命令时,可能会遇到war\target目录无法删除的问题,需先排查并解除目录占用,以确保编译顺利进行。
学习资源
对于有兴趣深入学习Java工程化、高性能及分布式、微服务、Spring、MyBatis、Netty源码分析的朋友,推荐加入交流群。群内有资深阿里专家直播讲解技术,并免费分享Java大型互联网技术的视频资源。
深入理解Netty编解码、粘包拆包、心跳机制
深入理解Netty编解码、粘包拆包、心跳机制
Netty编解码涉及的组件有Channel、ChannelHandler、ChannelPipe等。ChannelHandler主要用于处理入站和出站数据的应用程序逻辑。ChannelPipeline提供了一个ChannelHandler链的容器,例如客户端应用程序,事件的运动方向通常是从客户端到服务端,数据会通过pipeline中的ChannelOutboundHandler进行处理,反之称为入站,数据会通过pipeline中的ChannelInboundHandler进行处理。
编解码器在Netty中非常关键,用于在发送或接受消息时进行数据转换。Netty提供了一系列实用的编码解码器,例如StringEncoder和StringDecoder、ObjectEncoder和ObjectDecoder等。也可以通过集成ByteToMessageDecoder自定义编解码器。
在Netty中处理粘包拆包问题,可以通过在数据末尾添加特殊符号以标识边界,使用LineBasedFrameDecoder或自定义长度帧解码器等方式解决。
Google Protobuf是一种与语言无关、平台无关、可扩展的数据序列化机制。在RPC或TCP通信等场景中广泛使用。使用ProtoBuf可以将数据结构转化为字节流,方便在不同语言的程序间传输。通过添加maven依赖、编写.proto文件、使用编译器生成代码,可以在发送端和接收端分别添加编码器和解码器,实现高效的数据传输。
在使用Protocol编解码器时,仍有可能遇到粘包问题,可以通过在发送端添加ProtobufVarintLengthFieldPrepender编码器,在接收端添加ProtobufVarintFrameDecoder解码器来解决。
心跳机制在TCP长连接中非常重要,用于定期发送特殊数据包,确保连接的有效性。Netty中实现心跳机制的关键是IdleStateHandler,可通过设置channelActive方法触发ReaderIdleTimeoutTask,当连接长时间无活动时,会触发心跳事件,从而通知对方连接仍然活跃。
以上内容涵盖Netty的编解码、粘包拆包、心跳机制的理解和实现,通过实际代码示例来展示如何在服务端和客户端进行应用。
2024-12-28 22:35
2024-12-28 22:30
2024-12-28 21:53
2024-12-28 21:53
2024-12-28 21:49