当前位置: 首页 > article >正文

Unity网络基础UDP客户端

第一部分核心概念预习在看代码之前你需要明白这几个核心概念UDP (User Datagram Protocol)就像寄明信片。你只管把信发出去不需要先跟对方建立连接速度极快但不保证对方一定能收到也不保证顺序。适合做对实时性要求高的游戏如王者荣耀、吃鸡的移动同步。Socket套接字网络通信的基石。如果把网络比作公路Socket 就是跑在公路上的货车负责装载和卸载货物数据。多线程 (ThreadPool)Unity 的主线程Update负责画面的渲染。网络接收和发送如果放在主线程一旦网络卡顿游戏画面就会卡死。所以我们需要把网络收发放到后台去打工子线程。队列 (Queue)一种“先进先出”FIFO的数据结构。就像排队买票先排队的先买到。代码里用作消息的“收件箱”和“发件箱”。第二部分代码详解与注释我为你的代码加上了详尽的注释并解释了每个变量存在的意义。请对照着看using System; using System.Collections; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Threading; using UnityEngine; public class UdpNetMgr : MonoBehaviour { // 【单例模式】保证整个游戏只有一个网络管理器 private static UdpNetMgr instance; public static UdpNetMgr Instance instance; // 【核心变量说明】 // serverIpPoint: 记住服务器的地址和端口号也就是你要把数据发给谁。 private EndPoint serverIpPoint; // socket: 通信的工具就是前面比喻的“货车”。 private Socket socket; // sendQueue: 发件箱。你要发送的消息先排队放在这里。 private QueueBaseMsg sendQueue new QueueBaseMsg(); // receiveQueue: 收件箱。后台线程收到消息后先堆在这里。 private QueueBaseMsg receiveQueue new QueueBaseMsg(); // cacheBytes: 缓存区。用来存放每次从网络接收到的原始二进制字节大小512字节。 private byte[] cacheBytes new byte[512]; // isClose: 这是一个“开关”用来控制后台线程要不要继续干活。 private bool isClose true; void Awake() { instance this; // 切换场景时不销毁这个网络管理器保证网络一直连接 DontDestroyOnLoad(this.gameObject); } // 【主线程处理】 void Update() { // 为什么要在 Update 里处理消息 // 答因为 Unity 规定所有的游戏物体操作比如修改血量、移动位置必须在主线程执行 // 后台线程不能直接操作 Unity 的组件所以后台线程把消息放进 receiveQueue主线程来处理。 if(receiveQueue.Count 0) { // 从收件箱拿出一个消息 BaseMsg baseMsg receiveQueue.Dequeue(); switch (baseMsg) { // C# 的模式匹配语法如果是 PlayerMsg 类型的消息就把它赋值给 msg 变量 case PlayerMsg msg: print(msg.playerID); print(msg.playerData.name); print(msg.playerData.atk); print(msg.playerData.lev); break; } } } // 【启动客户端】告诉别人我是谁我要连谁 public void StartClient(string ip, int port) { if(!isClose) return; // 如果已经开启了就不重复开启 // 1. 记录服务器目标地址 serverIpPoint new IPEndPoint(IPAddress.Parse(ip),port); // 2. 设定客户端自己的地址和端口 (这里硬编码了本地127.0.0.1和8081端口) IPEndPoint clientIpPort new IPEndPoint(IPAddress.Parse(127.0.0.1),8081); try { // 3. 创建 UDP Socket (Dgram 代表数据报UDP专用) socket new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); // 4. 绑定自己的地址相当于给自己的邮箱挂上牌子告诉别人把信寄到这个端口 socket.Bind(clientIpPort); isClose false; // 打开线程开关 print(客户端网络启动); // 5. 将“接收”和“发送”任务扔给线程池后台运行不卡死主线程 ThreadPool.QueueUserWorkItem(ReceiveMsg); ThreadPool.QueueUserWorkItem(SendMsg); } catch(System.Exception e) { print(启动异常 e.Message); } } // 【后台线程接收消息】死循环一直盯着有没有信件来 private void ReceiveMsg(object obj) { // 临时变量用来记录是谁发给我的因为UDP任何人都能往这个端口发 EndPoint tempIpPoint new IPEndPoint(IPAddress.Any, 0); int nowIndex; int msgID; int msgLength; // 只要没关闭就一直循环 while (!isClose socket ! null) { // socket.Available 表示当前网卡里有没有别人发来的未读数据 if(socket.Available 0) { try { // 接收数据放入 cacheBytes并且把发送者的地址记录在 tempIpPoint 中 socket.ReceiveFrom(cacheBytes, ref tempIpPoint); // 校验如果发信人不是服务器就丢弃这封信防黑客捣乱 if (!tempIpPoint.Equals(serverIpPoint)) { continue; } // 【字节解析反序列化】 // 规定格式前4个字节是 消息ID - 接着4个字节是 消息长度 - 后面是 真实数据 nowIndex 0; msgID BitConverter.ToInt32(cacheBytes, nowIndex); // 读ID nowIndex 4; // 指针往后移动4个字节 msgLength BitConverter.ToInt32(cacheBytes , nowIndex); // 读长度 nowIndex 4; // 指针继续往后移动 BaseMsg msg null; // 根据不同的 ID 生成不同的消息对象 switch (msgID) { case 1: msg new PlayerMsg(); // 让消息自己去把剩下的字节解析成数据 msg.Reading(cacheBytes, nowIndex); break; } // 解析成功放入收件箱等待主线程 Update 去处理 if(msg ! null) receiveQueue.Enqueue(msg); } catch (SocketException s) { print(接收消息出问题 s.SocketErrorCode s.Message); } catch(Exception e) { print(接收消息出问题 非网络问题 e.Message); } } } } // 【后台线程发送消息】死循环一直盯着发件箱 private void SendMsg(object obj) { while (!isClose socket ! null) { // 如果发件箱里有东西 if(sendQueue.Count 0) { try { // 拿出一个消息转化为字节数组Writing方法发送给服务器 socket.SendTo(sendQueue.Dequeue().Writing(), serverIpPoint); } catch(SocketException s) { print(发送消息出错 s.SocketErrorCode s.Message); } } // 【老师注这里其实缺了一行很重要的代码下面扩展会讲】 } } // 【提供给其他脚本调用的发送接口】 public void Send(BaseMsg msg) { // 只是放进发件箱不直接发。后台的 SendMsg 线程会自动把它发出去。 sendQueue.Enqueue(msg); } // 【关闭连接与清理】 public void Close() { if(socket null ) return; // 发送最后一个消息告诉服务器“我下线了” QuitMgr msg new QuitMgr(); socket.SendTo(msg.Writing(), serverIpPoint); isClose true; // 关停两个后台线程 socket.Shutdown(SocketShutdown.Both); // 停止收发 socket.Close(); // 关闭 Socket socket null; } // 当这个游戏物体被销毁时比如游戏退出一定要关闭网络 private void OnDestroy() { Close(); } }第三部分总结、利弊与扩展避坑指南 作为你的老师我必须指出这段代码中的几个危险的雷区Bug因为如果不改它会引发严重的性能问题代码的优点利结构清晰采用了收发分离的队列思想逻辑上很整洁。多线程异步网络通信没有阻塞 Unity 的主线程不会掉帧。代码的缺点弊与修改方案重要 CPU 占用爆炸问题死循环陷阱问题在 SendMsg 方法中while (!isClose) 是一个无尽的循环。如果发件箱里没有消息它会一秒钟空转几百万次瞬间把你的电脑 CPU 单核占满 100%修复在没有消息时让线程“睡”一会儿。private void SendMsg(object obj) { while (!isClose socket ! null) { if(sendQueue.Count 0) { // 发送逻辑... } else { Thread.Sleep(5); // 让线程休息5毫秒极大地释放CPU性能 } } } 线程安全问题队列 Race Condition问题C# 原生的 QueueT不是线程安全的主线程在 Update 里 Dequeue取消息后台线程同时在 ReceiveMsg 里 Enqueue塞消息。两个线程同时抢夺一个变量大概率会引发程序崩溃修复使用 lock 关键字加锁或者使用线程安全队列 ConcurrentQueueT需要引入 System.Collections.Concurrent。// 修复发送队列接收队列同理 public void Send(BaseMsg msg) { lock(sendQueue) { sendQueue.Enqueue(msg); } } // 取出时也需要 lock lock(sendQueue) { msg sendQueue.Dequeue(); }硬编码绑定的问题问题StartClient 里写死了 127.0.0.1 和端口 8081。如果同一台电脑开两个客户端第二个就会因为 8081 端口被占用而报错崩溃。修复把客户端绑定的端口设为 0让操作系统自动分配一个空闲端口即可Q1:为什么我们接收网络数据要用 ThreadPool.QueueUserWorkItem而不是直接在 Update 函数里面调用 socket.ReceiveFrom()Q2:既然接收消息在后台线程为什么我们不能在 ReceiveMsg 函数里直接写 player.transform.position newPos 这样的代码来改变人物位置Q3:代码里的 isClose 这个 bool 变量起到了什么关键作用A1:因为 socket.ReceiveFrom() 可能会发生阻塞如果网络不好一直等不到消息或者即使使用 Available如果在 Update 里处理网络会消耗大量时间导致 Unity 主线程卡死游戏掉帧。放到后台可以避免影响游戏流畅度。A2:因为 Unity 引擎的底层规定了绝大多数的 UnityEngine API比如控制 Transform、修改 UI只能在主线程调用。后台线程强行调用会报错。所以我们要把数据放进 receiveQueue让主线程在 Update 里去拿数据并移动人物。A3:它是控制后台线程生死循环的开关。如果关掉游戏时OnDestroy 被调用不把 isClose 设为 true后台线程依然会像孤儿一样在内存里死循环导致游戏关闭后后台仍有进程残留。

相关文章:

Unity网络基础UDP客户端

第一部分:核心概念预习在看代码之前,你需要明白这几个核心概念:UDP (User Datagram Protocol):就像寄明信片。你只管把信发出去,不需要先跟对方建立连接,速度极快,但不保证对方一定能收到&#…...

feed二级缓存设计day05

背景:feed流:投喂流,主动把消息发给我们,类似于朋友圈别人的消息组成了我的主页feed流与内容详情是该社区访问最多的接口,面临着以下挑战:- **高并发读压力**:首页 Feed 与热门内容详情同一时刻…...

SpringBoot+Vue 办公信息系统管理平台源码免费分享【适合毕设/课设/学习】Java+Vue+MySQL

【源码在文章最后】 ## 项目概述 办公自动化系统(Office Automatic System,简称OA)是一个基于Spring Boot和Vue.js的企业级办公管理平台,旨在提高企业内部办公效率,实现办公流程的数字化和自动化。 ## 项目信息 - *…...

新能源汽车车载双向OBC,PFC,LLC,V2G 双向充电桩MATLAB仿真模型及应用分析

新能源汽车车载双向OBC,PFC,LLC,V2G 双向 充电桩 电动汽车 车载充电机 充放电机 MATLAB仿真模型 : (1)基于V2G技术的双向AC/DC、DC/DC充放电机MATLAB仿真模型; (2)前级电路为双向AC/…...

基于单片机的药品分拣系统

一、系统介绍 目前,随着当今社会老龄化进程的逐步加剧,我们在新闻中经常能看到老人跌倒了,无人扶,进而导致老人的死亡。对于这种悲剧,我们也很无奈,因为怕扶了老人,可能会被讹,老年人…...

qmt之如何用局部极值寻找支撑位和压力位(下)

书接上回,上一节是原理讲解,这里开始实战。实战Python代码:使用argrelextrema寻找压力支撑下面的示例代码使用了 Python 的 scipy.signal.argrelextrema 来寻找局部极值,并绘制成可视化图表。结果如下:代码解析&#x…...

国内外主流AI产品的能力矩阵解构,不同用户该怎么选择?

026年了,AI早就不是什么新鲜词儿了,它已经变成了我们工作和生活里的“搭子”。但说实话,现在的AI产品多到让人眼花缭乱,国内外各种工具层出不穷,选错了不仅浪费时间,还可能踩坑。今天我们就来唠唠现在市面上…...

吐血整理,性能测试总结分析,快速上手打通(一)

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、性能测试的测试…...

实测,2分钟完成OpenClaw部署全攻略,ToClaw傻瓜式安装

实测,2分钟完成OpenClaw部署全攻略,ToClaw傻瓜式安装最近,一只名叫OpenClaw的“龙虾”彻底火了——火到二手平台出现上门安装服务,腾讯大厦门口排起长队,甚至官媒都在热议。但传统的部署方式却让多数人望而却步&#x…...

给小龙虾装上业务大脑:两个 SKILL 让 OpenClaw 学会查数和归因

全网都在教小龙虾点外卖,我们让它算清了 GMV 为什么跌。这篇文章是配套的上手指南——从安装到提问到进阶调教,手把手带你跑通。 我们把小龙虾接上了 Aloudata CAN 语义层。六轮对话,从「上月销售额多少」一路追到「江西电商渠道销售额环比下…...

conda管理包还是pip管理包

1. Conda会自动处理依赖冲突我是用使用的是conda环境来python虚拟环境的,我创建了一个名叫ai的环境,我第一次进入环境后,先使用pip安装了一些包,然后发现由于版本冲突原因而下载失败,然后我又使用conda命令去下载这些包…...

基于SMO滑模观测器的异步电机无传感器矢量控制研究:Matlab仿真模型分析

基于SMO滑模观测器的异步电机无传感器矢量控制,matlab,仿真模型。无感矢量控制在工业界被玩得越来越溜,最近在实验室调了套基于滑模观测器(SMO)的方案。这玩意儿最大的爽点就是不需要速度传感器,靠算法硬怼…...

【Kotlin】快速理解协程

文章目录一、预备知识1.1 同步和异步1.1.1 同步1.1.2 异步1.2 异步编程1.2.1 异步编程的困境1.2.2 解决方案二、协程2.1 什么是协程2.1.1 协程的定义2.1.2 协程的特点2.2 结构化并发2.2.1 核心原则2.2.2 实现机制:Job 层次结构2.3 协程的基本概念2.3.1 协程和挂起函…...

南京租打印机别踩坑!押金透明、退机不扯皮才是王道

“很多企业以为‘租打印机只是看价格’,却忽略了押金和退机环节可能埋下的‘隐形坑’——这正是南京打印机复印机租赁市场中,专业服务商与普通商家的核心区别。”南京打印机复印机租赁:押金透明是专业服务的基础在南京打印机复印机租赁市场&a…...

告别AI Agent记忆混乱与幻觉!收藏这份RAG实战指南,小白也能轻松搞定大模型落地

本文深入剖析了AI Agent常见的记忆混乱和幻觉问题,并提出解决方案:通过RAG(检索增强生成)结合上下文工程,从外部知识库获取准确信息,优化上下文结构。文章详细介绍了向量数据库选型、Spring Boot集成Chroma…...

弃国外开源,创自主开源 Perseus:乐维的底层技术抉择与智能体战略

在IT运维监控领域,开源工具曾被视为降本提效的最优解,Prometheus、Zabbix 等凭借免费、生态丰富的优势占据大量市场份额。然而,乐维却选择放弃成熟的开源采集方案,历经 5 年迭代自研 Perseus 采集平台。这一决策并非对开源的否定&…...

CSDN Markdown自动发布踩坑记录与解决方案

CSDN Markdown自动发布踩坑记录与解决方案 最近把本地 Markdown 自动发布到 CSDN 这件事完整跑通了一遍,中间遇到了不少真实问题。 这篇文章主要记录整个排查过程、最终方案,以及后续使用时需要注意的几个点,方便后面继续维护。 一、目标 最开…...

Frida Hook实战:用JavaScript脚本拦截Android App的HttpURLConnection网络请求

Frida Hook实战:用JavaScript脚本拦截Android App的HttpURLConnection网络请求 在移动应用安全分析和逆向工程领域,动态插桩技术已经成为分析应用行为的利器。Frida作为其中的佼佼者,以其灵活的JavaScript脚本和强大的功能,让开发…...

基于java的人脸识别考勤管理系统的vue

目录系统架构设计后端实现要点前端Vue实现方案技术集成方案开发阶段划分注意事项项目技术支持可定制开发之功能创新亮点源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作系统架构设计 采用前后端分离架构,后端使用Java&#xff0…...

HCIE培训机构哪家更划算?如何选择才能不花冤枉钱!

在数字化转型加速推进的背景下,企业对ICT领域专业人才的需求持续上升,个人通过技能提升实现职业发展的意愿也日益明显。HCIE作为华为认证体系中的高级认证,涵盖数通、云计算、安全等多个方向,已成为不少职场人士和应届毕业生关注的…...

基于java大学生心理健康管理系统

目录技术选型核心功能模块数据存储设计扩展功能实现测试与部署注意事项项目技术支持可定制开发之功能创新亮点源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作技术选型 后端框架:Spring Boot(简化配置,内…...

从IO到NIO:Java文件操作的性能跃迁

在Java编程的早期阶段,我们依赖传统的BIO(Blocking I/O)进行文件读写操作。这种模式下,每个I/O请求都会占用一个线程,数据在磁盘、内核缓冲区、用户缓冲区之间反复拷贝,不仅线程开销大,还存在多…...

高效的触摸屏HMI配方管理与机种管理案例分享——以威纶触摸屏为例,结合宏指令实现复杂机种配方管理

触摸屏hmi配方管理机种管理 威纶触摸屏配方机种管理案例 本人实际项目上使用 结合宏指令可实现复杂机种配方管理去年在设备改造项目里遇到过头疼的问题:产线要同时处理12种不同型号的金属配件,每种型号对应15组工艺参数。操作工经常手抖选错参数&#xf…...

地埋式一体化污水处理设备优缺点

地埋式一体化污水处理设备优缺点 优点 节约土地与美观性:主体埋于地下,地表可用于绿化或建设停车场等,大幅节省土地资源,尤其适合土地紧张或对环境美观度要求高的区域,基本不影响地面空间的正常使用。 高效稳定的处理能…...

医用设备带:从基础生命支持终端到智慧医疗核心枢纽的演进之路

引言在现代化医院建设中,医用设备带作为临床诊疗区域的关键基础设施,正经历着从单一功能载体向智能化、集成化核心枢纽的深刻变革。它不仅是病房环境中不可或缺的组成部分,更是直接关联诊疗效率与患者体验的重要工程。随着智慧医院建设从概念…...

py每日spider案例之某website之music搜索接口(无加密)

import requestsheaders = {"accept": "application/json, text/plain, */*","accept-language": "zh-CN,zh;q=0.9","cache-control": "no-cache","origin":...

如何重置idea ai assistant ACP 插件中的 Cursor 账号登录状态?

在使用 JetBrains 系列 IDE(如 IntelliJ IDEA, PyCharm 等)配合 ACP (AI Assistant) 插件时,经常会遇到想要更换 Cursor 账号却“退出无门”的情况。即使卸载插件还是会重新登录之前的账号。 本文分享一个通过手动运行本地 Agent 脚本强制重…...

0 基础入门 Agent:理论知识体系搭建指南

本文档系统梳理 AI Agent 的核心理论知识,帮助理解”为什么这样设计”,为动手构建 Agent 打下认知基础。 1. 从 LLM 到 Agent:为什么需要 Agent 1.1 LLM 的能力与局限 大语言模型(LLM)本质上是一个”文本补全机器”…...

AI 时代,前端先死,还是后端先死?

当 Agent 开始接管一切,写按钮的和写接口的,谁先失业?引子:一场程序员的世纪之争 2025 年某个深夜,某互联网公司茶水间。 前端工程师小王正往杯子里倒第三包速溶咖啡,后端工程师老张靠在饮水机旁刷手机。…...

亲测好用 9个降AIGC平台全场景通用测评,哪个最能帮你降AI率?

在学术写作和内容创作领域,AI生成内容(AIGC)的广泛应用带来了前所未有的便利,但同时也引发了对原创性和查重率的关注。对于需要提交高质量论文或文章的用户来说,降低AIGC率、去除AI痕迹、提升文本原创性已成为一项重要…...