Unity网络通信(part7.分包和黏包)
目录
前言
概念
解决方案
具体代码
总结
分包黏包概念
分包
黏包
解决方案概述
前言

在探讨Unity网络通信的深入内容时,分包和黏包问题无疑是其中的关键环节。以下是对Unity网络通信中分包和黏包问题前言部分的详细解读。
概念
在网络通信中,分包和黏包是常见的问题。分包是指将一个较大的数据包拆分成多个小的数据包进行传输,黏包则是指将多个小的数据包合并成一个较大的数据包。
产生分包和黏包的原因主要有两个:一是网络传输的不可靠性,数据包在传输过程中可能会丢失、重复或乱序;二是数据发送方将多个数据包连续发送,而接收方可能不会立即处理完一个数据包,而是先处理下一个数据包,导致多个数据包被合并成一个。
解决分包和黏包问题的方法有多种,其中一种常见的方法是在数据包中添加特殊的标记,来标识数据包的边界,以便接收方能够正确地解析出每个数据包。
在Unity中,可以使用自定义的消息协议来解决分包和黏包问题。具体的实现方式可以根据实际需求来确定,比如可以在数据包的头部添加一个表示数据包长度的字段,或者在数据包之间添加一个特定的分隔符来标记数据包的边界。
除了使用自定义的消息协议,还可以使用Unity提供的网络组件来解决分包和黏包问题。比如可以使用Unity自带的网络库UNet,或者使用第三方网络库如Photon Unity Networking(PUN)来进行网络通信。这些网络库都提供了相应的接口和方法来处理分包和黏包问题。
总之,分包和黏包是网络通信中常见的问题,但可以通过合适的方法和工具来解决。在开发网络游戏或其他网络应用时,需要注意处理分包和黏包问题,以确保数据的正确传输和解析。

注意:分包和黏包可能同时发生
解决方案
1.为所有消息添加头部信息,用于存储其消息长度
2.根据分包、黏包的表现情况,修改接收消息处的逻辑
具体代码
此代码在之前的客户端管理模块基础上将接收消息的方法做了改动。
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using UnityEngine;
using UnityEngine.XR;public class NetMgr : MonoBehaviour
{private static NetMgr instance;public static NetMgr Instance => instance;//客户端Socketprivate Socket socket;//用于发送消息的队列 公共容器 主线程往里面放 发送线程往里面取private Queue<BaseMsg> sendMsgQueue = new Queue<BaseMsg>();//用于接收消息的队列 公共容器 子线程往里面放 主线程往里面取private Queue<BaseMsg> receiveQueue = new Queue<BaseMsg>();用于收消息的容器//private byte[] receiveBytes = new byte[1024*1024];返回收到的字节数//private int receiveNum;//用于处理分包时缓存的字节数private byte[] cacheBytes = new byte[1024*1024];private int cacheNum;//是否连接private bool isConnect=false;private void Awake(){instance = this; DontDestroyOnLoad(this.gameObject);}private void Update(){if(receiveQueue.Count>0){BaseMsg msg = receiveQueue.Dequeue();if(msg is PlayerMsg){PlayerMsg playerMsg = (PlayerMsg)msg;print(playerMsg.playerID);print(playerMsg.playerData.name);print(playerMsg.playerData.lev);print(playerMsg.playerData.atk);}}}//连接服务端public void Connect(string ip,int port){//如果是连接状态 直接返回if(isConnect){return;}if(socket==null){socket = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);}//连接服务端IPEndPoint iPPoint = new IPEndPoint(IPAddress.Parse(ip),port);try{socket.Connect(iPPoint);isConnect=true;//开启发送线程ThreadPool.QueueUserWorkItem(SendMsg);//开启接收线程ThreadPool.QueueUserWorkItem(ReceiveMsg);}catch(SocketException e){if(e.ErrorCode == 10061){print("服务器拒绝连接");}else{print("连接失败"+e.ErrorCode+e.Message);}}}//发送消息public void Send(BaseMsg msg){sendMsgQueue.Enqueue(msg);}private void SendMsg(object obj){while(isConnect){if(sendMsgQueue.Count>0){socket.Send(sendMsgQueue.Dequeue().Writing());}}}//接收消息public void ReceiveMsg(object obj){while(isConnect){if (socket.Available > 0){//申明为临时变量,节约内存空间byte[] receiveBytes = new byte[1024 * 1024];int receiveNum = socket.Receive(receiveBytes);HandleReceiveMsg(receiveBytes,receiveNum);首先把收到字节数组的前4个字节 读取出来得到ID//int msgID = BitConverter.ToInt32(receiveBytes, 0);//BaseMsg baseMsg = null;//switch (msgID)//{// case 1001:// PlayerMsg msg = new PlayerMsg();// msg.Reading(receiveBytes, 4);// baseMsg = msg;// break;//}如果消息为空 那证明是不知道类型的消息 没有解析//if (baseMsg == null)//{// continue;//}收到消息 解析消息为字符串 并放入公共容器//receiveQueue.Enqueue(baseMsg);}}}//处理接收消息 分包、黏包问题的方法private void HandleReceiveMsg(byte[] receiveBytes,int receiveNum){int msgID=0;int msgLength = 0;int nowIndex = 0;//收到消息时,应该看看之前有没有缓存的 如果有的话 直接拼接到后面receiveBytes.CopyTo(cacheBytes,cacheNum);cacheNum += receiveNum;while(true){//每次将长度设置为-1,是为了避免上一次解析的数据影响这一次的判断msgLength = -1;//处理解析一条消息if(cacheNum-nowIndex >= 8){//解析IDmsgID = BitConverter.ToInt32(cacheBytes, nowIndex);nowIndex += 4;//解析长度msgLength = BitConverter.ToInt32(cacheBytes, nowIndex);nowIndex += 4;}if(cacheNum - nowIndex>=msgLength&&msgLength!=-1){//解析消息体BaseMsg baseMsg = null;switch (msgID){case 1001:PlayerMsg msg = new PlayerMsg();msg.Reading(cacheBytes, nowIndex);baseMsg = msg;break;}if (baseMsg != null){receiveQueue.Enqueue(baseMsg);}nowIndex += msgLength;if(nowIndex == cacheNum){cacheNum = 0;break;}}else//保存消息体,等下一次收到消息时进行拼接{//receiveBytes.CopyTo(cacheBytes, 0);//cacheNum = receiveNum;//如果进行了id和长度的解析 但是 没有成功jie'xi'xiao'xi'tiif(msgLength !=-1){nowIndex -= 8;}//就是把剩余没有解析字节数组内容 移到前面来 用来缓存下次继续解析Array.Copy(cacheBytes,nowIndex,cacheBytes,0,cacheNum-nowIndex);cacheNum = cacheNum - nowIndex;break;}}}//关闭连接public void Close(){if(socket!=null){socket.Shutdown(SocketShutdown.Both);socket.Close();isConnect = false;}}private void OnDestroy(){Close();}
}
总结
分包黏包概念
分包
分包是指一个完整的消息在发送过程中被拆分成了多个消息包进行发送。例如,原本的一个字节数组B,被分成了两段(或更多),如字节数组B1和字节数组B2。
黏包
黏包则是指多个消息在发送过程中合并成了一个消息包进行发送。例如,消息A的字节数组A和消息B的字节数组B在发送过程中黏在了一起,形成了一个新的字节数组,其长度为两者之和。
解决方案概述
- 添加消息头部:
- 为每个消息添加头部信息,头部中记录该消息的长度。
- 当接收到消息时,首先读取头部信息,根据头部中记录的长度来判断消息是否完整,以及是否出现了分包或黏包的情况。
- 消息处理逻辑:
- 在接收消息时,需要维护一个缓存区,用于存储接收到的字节数据。
- 每次接收到新的字节数据时,将其追加到缓存区中。
- 然后,根据消息头部中记录的长度,从缓存区中逐个解析出完整的消息。
- 如果缓存区中的数据不足以构成一个完整的消息,则继续等待接收新的字节数据。
- 如果缓存区中的数据可以构成一个或多个完整的消息,则依次解析出这些消息,并将其从缓存区中移除。
相关文章:
Unity网络通信(part7.分包和黏包)
目录 前言 概念 解决方案 具体代码 总结 分包黏包概念 分包 黏包 解决方案概述 前言 在探讨Unity网络通信的深入内容时,分包和黏包问题无疑是其中的关键环节。以下是对Unity网络通信中分包和黏包问题前言部分的详细解读。 概念 在网络通信中,…...
练习题 - DRF 3.x Overviewses 框架概述
Django REST Framework (DRF) 是一个强大的工具,用于构建 Web APIs。作为 Django 框架的扩展,DRF 提供了丰富的功能和简洁的 API,使得开发 RESTful Web 服务变得更加轻松。对于想要在 Django 环境中实现快速且灵活的 API 开发的开发者来说,DRF 是一个非常有吸引力的选择。学…...
Linux 经典面试八股文
快速鉴别十个题 1,你如何描述Linux文件系统的结构? 答案应包括对/, /etc, /var, /home, /bin, /lib, /usr, 和 /tmp等常见目录的功能和用途的描述。 2,在Linux中如何查看和终止正在运行的进程? 期望的答案应涵盖ps, top, htop, …...
Filter和Listener
一、Filter过滤器 1 概念 可以实现拦截功能,对于指定资源的限定进行拦截,替换,同时还可以提高程序的性能。在Web开发时,不同的Web资源中的过滤操作可以放在同一个Filter中完成,这样可以不用多次编写重复代码…...
Go 项目中实现类似 Java Shiro 的权限控制中间件?
序言: 要在 Go 项目中实现类似 Java Shiro 的权限控制中间件,我们可以分为几个步骤来实现用户的菜单访问权限和操作权限控制。以下是一个基本的实现框架步骤: 目录 一、数据库设计 二、中间件实现 三、使用中间件 四、用户权限管理 五…...
【Javascript】-一些原生的网页设计案例
JavaScript 网页设计案例 1. 动态时钟 功能描述:在网页上显示一个动态更新的时钟,包括小时、分钟和秒。实现思路: 使用 setInterval 函数每秒更新时间。获取当前时间并更新页面上的文本。 代码示例:<div id"clock"…...
SpringBoot开发——Spring Boot 3种定时任务方式
文章目录 一、什么是定时任务二、代码示例1、 @Scheduled 定时任务2、多线程定时任务3、基于接口(SchedulingConfigurer)实现动态更改定时任务3.1 数据库中存储cron信息3.2 pom.xml文件中增加mysql依赖3.3 application.yaml文件中增加mysql数据库配置:3.4 创建定时器3.5 启动…...
Flutter鸿蒙next 实现长按录音按钮及动画特效
在 Flutter 中实现长按录音按钮并且添加动画特效,是一个有趣且实用的功能。本文将通过实现一个具有动画效果的长按录音按钮,带领你一步步了解如何使用 Flutter 完成这个任务,并解释每一部分的实现。 一、功能需求 我们需要一个按钮…...
【计网】实现reactor反应堆模型 --- 框架搭建
没有一颗星, 会因为追求梦想而受伤, 当你真心渴望某样东西时, 整个宇宙都会来帮忙。 --- 保罗・戈埃罗 《牧羊少年奇幻之旅》--- 实现Reactor反应堆模型 1 前言2 框架搭建3 准备工作4 Reactor类的设计5 Connection连接接口6 回调方法 1 …...
力扣中等难度热题——长度为K的子数组的能量值
目录 题目链接:3255. 长度为 K 的子数组的能量值 II - 力扣(LeetCode) 题目描述 示例 提示: 解法一:通过连续上升的长度判断 Java写法: C写法: 相比与Java写法的差别 运行时间 时间复杂…...
JSON格式
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人和机器阅读和解析。它基于JavaScript的对象表示法,但被广泛用于多种编程语言。 JSON中的数据类型 字符串(String):用双引…...
O-RAN前传Spilt Option 7-2x
Spilt Option 7-2x 下行比特处理上行比特处理相关文章: Open Fronthaul wrt ORAN 联盟被称为下层拆分(LLS),其目标是提高电信市场的灵活性和竞争力。下层拆分是指无线电单元(RU) 和分布式单元(DU) 之间的拆分。 O-RAN前传接口可以在 eCPRI 上传输。eCPR…...
【GeoJSON在线编辑平台】(2)吸附+删除+挖孔+扩展
前言 在上一篇的基础上继续开发,补充上吸附功能、删除矢量、挖孔功能。 实现 1. 吸附 参考官方案例:Snap Interaction 2. 删除 通过 removeFeature 直接移除选中的要素。 3. 挖孔 首先是引入 Turf.js ,然后通过 mask 方法来实现挖孔的…...
确定图像的熵和各向异性 Halcon entropy_gray 解析
1、图像的熵 1.1 介绍 图像熵(image entropy)是图像“繁忙”程度的估计值,它表示为图像灰度级集合的比特平均数,单位比特/像素,也描述了图像信源的平均信息量。熵指的是体系的混乱程度,对于图像而言&#…...
大数据-214 数据挖掘 机器学习理论 - KMeans Python 实现 算法验证 sklearn n_clusters labels
点一下关注吧!!!非常感谢!!持续更新!!! 目前已经更新到了: Hadoop(已更完)HDFS(已更完)MapReduce(已更完&am…...
算法通关(3) -- kmp算法
KMP算法的原理 从题目引出 有两个字符串s1和s2,判断s1字符串是否包含s2字符串,如果包含返回s1包含s2的最左开头位置,不包含返回-1,如果是按照暴力的方法去匹配,以s1的每个字符作为开头,用s2的整体去匹配,…...
5G网卡network connection: disconnected
日志 5G流程中没有报任何错误,但是重新拿地址了,感觉像是驱动层连接断开了,dmesg中日志如下: [ 1526.558377] ippassthrough:set [ ip10.108.40.47 mask27 ip_net10.108.40.32 router10.108.40.33 dns221.12.1.227 221.12.33.227] br-lan […...
微积分复习笔记 Calculus Volume 1 - 4.9 Newton’s Method
4.9 Newton’s Method - Calculus Volume 1 | OpenStax...
Flutter自定义矩形进度条实现详解
在Flutter应用开发中,进度条是一个常见的UI组件,用于展示任务的完成进度。本文将详细介绍如何实现一个支持动画效果的自定义矩形进度条。 功能特点 支持圆角矩形外观平滑的动画过渡效果可自定义渐变色可配置边框宽度和颜色支持进度更新动画 实现原理 …...
如何设置 TORCH_CUDA_ARCH_LIST 环境变量以优化 PyTorch 性能
引言 在深度学习领域,PyTorch 是一个广泛使用的框架,它允许开发者高效地构建和训练模型。为了充分利用你的 GPU 硬件,正确设置 TORCH_CUDA_ARCH_LIST 环境变量至关重要。这个变量告诉 PyTorch 在构建过程中应该针对哪些 CUDA 架构版本进行优…...
云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
Hive 存储格式深度解析:从 TextFile 到 ORC,如何选对数据存储方案?
在大数据处理领域,Hive 作为 Hadoop 生态中重要的数据仓库工具,其存储格式的选择直接影响数据存储成本、查询效率和计算资源消耗。面对 TextFile、SequenceFile、Parquet、RCFile、ORC 等多种存储格式,很多开发者常常陷入选择困境。本文将从底…...
GruntJS-前端自动化任务运行器从入门到实战
Grunt 完全指南:从入门到实战 一、Grunt 是什么? Grunt是一个基于 Node.js 的前端自动化任务运行器,主要用于自动化执行项目开发中重复性高的任务,例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的“no matching...“系列算法协商失败问题
【SSH疑难排查】轻松解决新版OpenSSH连接旧服务器的"no matching..."系列算法协商失败问题 摘要: 近期,在使用较新版本的OpenSSH客户端连接老旧SSH服务器时,会遇到 "no matching key exchange method found", "n…...
R 语言科研绘图第 55 期 --- 网络图-聚类
在发表科研论文的过程中,科研绘图是必不可少的,一张好看的图形会是文章很大的加分项。 为了便于使用,本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中,获取方式: R 语言科研绘图模板 --- sciRplothttps://mp.…...
渗透实战PortSwigger靶场:lab13存储型DOM XSS详解
进来是需要留言的,先用做简单的 html 标签测试 发现面的</h1>不见了 数据包中找到了一个loadCommentsWithVulnerableEscapeHtml.js 他是把用户输入的<>进行 html 编码,输入的<>当成字符串处理回显到页面中,看来只是把用户输…...
