用Unity实现UDP客户端同步通信

news2025/3/31 8:59:34

制作UDPNetMgr网络管理模块

这段代码定义了一个名为UDPNetMgr的 Unity 脚本类,用于管理 UDP 网络通信,它作为单例存在,在Awake方法中创建收发消息的线程,Update方法处理接收到的消息;StartClient方法启动客户端连接,ReceiveMsgSendMsg方法分别用于接收和发送消息,Send方法将消息加入发送队列,Close方法关闭套接字并发送退出消息,同时在脚本销毁时自动调用关闭操作

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;

    private EndPoint serverIpPoint;

    public Socket socket;

    private bool IsClose=true;
    public byte[] cacheBytes = new byte[512];
    //创建两个队列来收消息和发消息
    public Queue<BaseMsg> sendMsgQueue = new Queue<BaseMsg>();
    public Queue<BaseMsg> receiveMsgQueue = new Queue<BaseMsg>();
    void Awake()
    {
        instance = this;
        DontDestroyOnLoad(this.gameObject);
        //创建两个线程来执行收发消息
        ThreadPool.QueueUserWorkItem(ReceiveMsg);
        ThreadPool.QueueUserWorkItem(SendMsg);
    }

    // Update is called once per frame
    void Update()
    {
        if(receiveMsgQueue .Count >0)
        {
            BaseMsg basemsg = receiveMsgQueue.Dequeue();
            switch (basemsg )
            {
                case PlayerMsg msg:
                    print(msg.playerID);
                    print(msg.playerData .atk);
                    print(msg.playerData .lev);
                    print(msg.playerData .name);
                    break;
            }
        }
    }
    //启动客户端Socket的方法
    public void StartClient(string ip,int port)
    {
        if (!IsClose)
            return;
        //记录远端服务器的Ip和端口号
        serverIpPoint = new IPEndPoint(IPAddress.Parse(ip), port);
        IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 8081);
        try
        {
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            socket.Bind(ipPoint);
            IsClose = false;
            print("客户端网络启动");
        }
        catch (System.Exception e)
        {
            print("启动客户端Socket失败" + e.Message);
        }
    }
    //接收消息的方法
    public void ReceiveMsg(object obj)
    {
        EndPoint ipEndPoint = new IPEndPoint(IPAddress.Any, 0);
        int msgID;
        int nowIndex;
        int msgLength;
        while (socket!=null&&!IsClose)
        {
            try
            {
                socket.ReceiveFrom(cacheBytes, ref ipEndPoint);
                //为了避免非服务器发来的骚扰消息
                if (!ipEndPoint.Equals(serverIpPoint))
                    continue;//如果不是服务器发来的消息,就不处理了
                //处理服务器发来的消息
                nowIndex = 0;
                //解析ID
                msgID = BitConverter.ToInt32(cacheBytes, nowIndex);
                nowIndex += 4;
                //解析消息长度
                msgLength = BitConverter.ToInt32(cacheBytes, nowIndex);
                nowIndex += 4;
                //解析消息体
                BaseMsg msg = null;
                switch (msgID)
                {
                    case 1001:
                        msg = new PlayerMsg();
                        //反序列化消息体
                        msg.Reading(cacheBytes, nowIndex);
                        break;
                }
                if (msg != null)
                    receiveMsgQueue.Enqueue(msg);
            }
            catch (SocketException s)
            {
                print("接收消息出问题" + s.SocketErrorCode + s.Message);
            }
            catch (System.Exception e)
            {
                print("接收消息出问题(非网络问题)" + e.Message);
            }
        }
    }
    //发送消息的方法
    public void SendMsg(object obj)
    {
        while (socket!=null&&!IsClose)
        {
            if(sendMsgQueue .Count >0)
            {
                try
                {
                    socket.SendTo(sendMsgQueue.Dequeue().Writing(), serverIpPoint);
                }
                catch (SocketException e)
                {
                    print("发送消息失败" + e.SocketErrorCode + e.Message);
                }
            }
        }
    }
    public void Send(BaseMsg msg)
    {
        sendMsgQueue.Enqueue(msg);
    }
    //关闭socket
    public void Close()
    {
        if(socket!=null)
        {
            IsClose = true;
            quitMsg quitMsg = new quitMsg();
            //发送一个退出消息给服务器,让他移除记录
            socket.SendTo(quitMsg.Writing(), serverIpPoint);
            socket.Shutdown(SocketShutdown.Both);
            socket.Close();
        }
    }
    private void OnDestroy()
    {
        Close();
    }
}

启动UDP客户端

MainUDP类继承自MonoBehaviour,是一个 Unity 脚本。它的主要功能是在游戏开始时确保UDPNetMgr单例实例存在,然后启动 UDP 客户端并连接到指定的服务器地址和端口。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MainUDP : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        if(UDPNetMgr .Instance ==null)
        {
            GameObject obj = new GameObject("UdpNet");
            obj.AddComponent<UDPNetMgr>();
        }
        UDPNetMgr.Instance.StartClient(("127.0.0.1"), 8080);
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

让客户端给服务器发消息

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Lesson15 : MonoBehaviour
{
    public Button btn;
    // Start is called before the first frame update
    void Start()
    {
        btn.onClick.AddListener(() =>
        {
            PlayerMsg playerMsg = new PlayerMsg();
            playerMsg.playerID = 1;
            playerMsg.playerData.atk = 888;
            playerMsg.playerData.lev = 999;
            playerMsg.playerData.name = "DamnF客户端发来的消息";
            UDPNetMgr.Instance.Send(playerMsg);
        });
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

实现客户端和服务器同步通信

运行服务器,启动客户端,使客户端和服务器实现UDP同步通信

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2323230.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pandoc安装及基础使用

pandoc安装 访问pandoc tags,切换至想要安装的版本&#xff0c;本次安装3.6.4 下载windows版本 下载texlive镜像&#xff0c;将文件转换成pdf需要用到 点开后会进入最近的镜像网站 下载完成后解压iso文件&#xff0c;以管理员身份运行install-tl-windows.bat&#xff…

3.27学习总结 算法题

自己用c语言做的&#xff0c;不尽如意 后面看了题解&#xff0c;用的是c&#xff0c;其中string 变量和字符串拼接感觉比c方便好多&#xff0c;可以用更少的代码实现更好的效果&#xff0c;打算之后去学习c&#xff0c;用c写算法。 递归&#xff0c;不断输入字符&#xff0c;…

案例分享|树莓派媒体播放器,重构商场广告的“黄金三秒”

研究显示&#xff0c;与传统户外广告相比&#xff0c;数字户外广告在消费者心中的记忆率提高了17%&#xff0c;而动态户外广告更是能提升16%的销售业绩&#xff0c;整体广告效率提升了17%。这一显著优势&#xff0c;使得越来越多资源和技术流入数字广告行业。 户外裸眼3D广告 无…

Redisson - 分布式锁和同步器

文章目录 锁&#xff08;Lock&#xff09;公平锁&#xff08;Fair Lock&#xff09;联锁&#xff08;MultiLock&#xff09;红锁&#xff08;RedLock&#xff09; 【已废弃】读写锁&#xff08;ReadWriteLock&#xff09;信号量&#xff08;Semaphore&#xff09;可过期许可信号…

Zustand 状态管理:从入门到实践

Zustand 状态管理&#xff1a;从入门到实践 Zustand 是一个轻量、快速且灵活的 React 状态管理库。它基于 Hooks API&#xff0c;提供了简洁的接口来创建和使用状态&#xff0c;同时易于扩展和优化。本文将通过一个 TODO 应用实例带你快速入门 Zustand&#xff0c;并探讨其核心…

PGP实现简单加密教程

模拟情景&#xff1a; 假设001和002两位同学的电脑上都安装了PGP&#xff0c;现在两人需要进行加密通讯。 一、创建密钥 1.新建密钥&#xff0c;输入名称和邮箱&#xff0c;输入8位口令&#xff0c;根据指示完成。 2.将其添加到主密钥&#xff0c;鼠标右击出现选项。 这里出…

7.8 窗体间传递数据

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的 当项目中有多个窗体时&#xff08;在本节中为两个窗体&#xff1a;Form1和Form2&#xff09;&#xff0c;窗体间传递数据有以下几种方…

【redis】集群 数据分片算法:哈希求余、一致性哈希、哈希槽分区算法

文章目录 什么是集群数据分片算法哈希求余分片搬运 一致性哈希扩容 哈希槽分区算法扩容相关问题 什么是集群 广义的集群&#xff0c;只要你是多个机器&#xff0c;构成了分布式系统&#xff0c;都可以称为是一个“集群” 前面的“主从结构”和“哨兵模式”可以称为是“广义的…

基于Springboot的网上订餐系统 【源码】+【PPT】+【开题报告】+【论文】

网上订餐系统是一个基于Java语言和Spring Boot框架开发的Web应用&#xff0c;旨在为用户和管理员提供一个便捷的订餐平台。该系统通过简化餐饮订购和管理流程&#xff0c;为用户提供快速、高效的在线订餐体验&#xff0c;同时也为管理员提供完善的后台管理功能&#xff0c;帮助…

【redis】集群 如何搭建集群详解

文章目录 集群搭建1. 创建目录和配置2. 编写 docker-compose.yml完整配置文件 3. 启动容器4. 构建集群超时 集群搭建 基于 docker 在我们云服务器上搭建出一个 redis 集群出来 当前节点&#xff0c;主要是因为我们只有一个云服务器&#xff0c;搞分布式系统&#xff0c;就比较…

飞牛NAS本地部署小雅Alist结合内网穿透实现跨地域远程在线访问观影

文章目录 前言1. VMware安装飞牛云&#xff08;fnOS&#xff09;1.1 打开VMware创建虚拟机1.3 初始化系统 2. 飞牛云搭建小雅Alist3. 公网远程访问小雅Alist3.1 安装Cpolar内网穿透3.2 创建远程连接公网地址 4. 固定Alist小雅公网地址 前言 嘿&#xff0c;小伙伴们&#xff0c…

Linux版本控制器Git【Ubuntu系统】

文章目录 **前言**一、版本控制器二、Git 简史三、安装 Git四、 在 Gitee/Github 创建项目五、三板斧1、git add 命令2、git commit 命令3、git push 命令 六、其他1、git pull 命令2、git log 命令3、git reflog 命令4、git stash 命令 七、.ignore 文件1、为什么使用 .gitign…

browser-use 库网页元素点击测试工具

目录 代码代码解释输出结果 代码 import asyncio import jsonfrom browser_use.browser.browser import Browser, BrowserConfig from browser_use.dom.views import DOMBaseNode, DOMElementNode, DOMTextNode from browser_use.utils import time_execution_syncclass Eleme…

解决GitLab无法拉取项目

1、验证 SSH 密钥是否已生成 ls ~/.ssh/ 如果看到类似 id_rsa 和 id_rsa.pub 的文件&#xff0c;则说明已存在 SSH 密钥。 避免麻烦&#xff0c;铲掉重来最方便。 如果没有&#xff0c;请生成新的 SSH 密钥&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexam…

FPGA学习篇——Verilog学习之寄存器的实现

1 寄存器理论 这里在常见的寄存器种加了一个复位信号sys_rst_n。&#xff08;_n后缀表示复位信号低电平有效&#xff0c;无这个后缀的则表示高电平有效&#xff09; 这里规定在时钟的上升沿有效&#xff0c;只有当时钟的上升沿来临时&#xff0c;输出out 才会改变&#xff0c;…

【VUE】ant design vue实现表格table上下拖拽排序

适合版本&#xff1a;ant design vue 1.7.8 实现效果&#xff1a; 代码&#xff1a; <template><div class"table-container"><a-table:columns"columns":dataSource"tableData":rowKey"record > record.id":row…

Vue实现动态数据透视表(交叉表)

需求:需要根据前端选择的横维度、竖维度、值去生成一个动态的表格&#xff0c;然后把交叉的值放入到对应的横维度和竖维度之下&#xff0c;其实就是excel里面的数据透视表功能&#xff0c;查询交叉语句为sql语句。 实现页面&#xff1a; 选择一下横维度、竖维度、值之后点击查…

推荐《人工智能算法》卷1、卷2和卷3 合集3本书(附pdf电子书下载)

今天&#xff0c;咱们就一同深入探讨人工智能算法的卷1、卷2和卷3&#xff0c;看看它们各自蕴含着怎样的奥秘&#xff0c;并且附上各自的pdf电子版免费下载地址。 《人工智能算法&#xff08;卷1&#xff09;&#xff1a;基础算法》 下载地址&#xff1a;https://www.panziye…

元宇宙浪潮下,数字孪生如何“乘风破浪”?

在当今科技飞速发展的时代&#xff0c;元宇宙的概念如同一颗璀璨的新星&#xff0c;吸引了全球的目光。元宇宙被描绘为一个平行于现实世界、又与现实世界相互影响且始终在线的虚拟空间&#xff0c;它整合了多种前沿技术&#xff0c;为人们带来沉浸式的交互体验。而数字孪生&…

数据分析 之 怎么看懂图 一

韦恩图怎么看 ①颜色:不同颜色代表不同的集合 ②)颜色重叠部分:表示相交集合共有的元素 ③颜色不重叠的部分:表示改集合独有的元素 ④数字:表示集合独有或共有的元素数量 ⑤百分比:表示该区域元素数占整体的比例 PCA图怎么看 ① 第一主成分坐标轴及主成分贡献率主成分贡献…