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 架构版本进行优…...

CSS的三个重点
目录 1.盒模型 (Box Model)2.位置 (position)3.布局 (Layout)4.低代码中的这些概念 在学习CSS时,有三个概念需要重点理解,分别是盒模型、定位、布局 1.盒模型 (Box Model) 定义: CSS 盒模型是指每个 HTML 元素在页面上被视为一个矩形盒子。…...

【笔记】前后端互通中前端登录无响应
后来的前情提要 : 后端的ip地址在本地测试阶段应该设置为localhost 前端中写cors的配置 后端也要写cors的配置 且两者的url都要为localhost 前端写的baseUrl是指定对应的后端的ip地址以及端口号 很重要 在本地时后端的IP的地址也必须为本地的 F12的网页报错是&a…...

AI引领PPT创作:迈向“免费”时代的新篇章?
AI引领PPT创作:迈向“免费”时代的新篇章? 在信息爆炸的时代,演示文稿(PPT)作为传递信息和展示观点的重要工具,其制作效率和质量直接关系到演讲者的信息传递效果。随着人工智能(AI)…...

HTB:Perfection[WriteUP]
目录 连接至HTB服务器并启动靶机 1.What version of OpenSSH is running? 使用nmap对靶机TCP端口进行开放扫描 2.What programming language is the web application written in? 使用浏览器访问靶机80端口页面,并通过Wappalyzer查看页面脚本语言 3.Which e…...

鸿蒙next打包流程
目录 下载团结引擎 添加开源鸿蒙打包支持 打包报错 路径问题 安装DevEcoStudio 可以在DevEcoStudio进行打包hap和app 包结构 没法直接用previewer运行 真机运行和测试需要配置签名,DevEcoStudio可以自动配置, 模拟器安装hap提示报错 安装成功,但无法打开 团结1.3版本新增工具…...

uni-app 实现自定义底部导航
原博:https://juejin.cn/post/7365533404790341651 在开发微信小程序,通常会使用uniapp自带的tabBar实现底部图标和导航,但现实有少量应用使用uniapp自带的tabBar无法满足需求,这时需要自定义底部tabBar功能。 例如下图的需求&am…...

Vue前端开发:animate.css第三方动画库
在实际的项目开发中,如果自定义元素的动画,不仅效率低下,代码量大,而且还存在浏览器的兼容性问题,因此,可以借助一些优秀的第三动画库来协助完成动画的效果,如animate.css和gsap动画库ÿ…...

Java中的I/O模型——BIO、NIO、AIO
1. BIO(Blocking I/O) 1. 1 BIO(Blocking I/O)模型概述 BIO,即“阻塞I/O”(Blocking I/O),是一种同步阻塞的I/O模式。它的主要特点是,当程序发起I/O请求(比如…...

【软考知识】敏捷开发与统一建模过程(RUP)
敏捷开发模式 概述敏捷开发的主要特点包括:敏捷开发的常见实践包括:敏捷开发的优势:敏捷开发的挑战:敏捷开发的方法论: ScrumScrum 的核心概念Scrum 的执行过程Scrum 的适用场景 极限编程(XP)核…...

Redis常见面试题(二)
Redis性能优化 Redis性能测试 阿里Redis性能优化 使用批量操作减少网络传输 Redis命令执行步骤:1、发送命令;2、命令排队;3、命令执行;4、返回结果。其中 1 与 4 消耗时间 --> Round Trip Time(RTT,…...