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

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

制作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同步通信

相关文章:

  • 基于云服务器的数仓搭建-hive/spark安装
  • Linux达梦数据库异地备份
  • STM32硬件IIC与OLED使用
  • Mininet--log.py-单例日志器-super().__new__(cls)解析
  • Dify 0.15.3版本 本地部署指南
  • 【已开源】UniApp+Vue3+TS全栈实战:从0到1构建企业级跨端应用与规范、uniapp+vue3模板应用
  • MySQL中如何进行SQL调优?
  • Windows 新型零日漏洞:远程攻击可窃取 NTLM 凭证,非官方补丁已上线
  • 【java笔记】泛型、包装类、自动装箱拆箱与缓存机制
  • 运维工程师学习知识总结
  • 生物化学笔记:医学免疫学原理07 补体系统+补体系统的激活+补体激活的调节+补体的生物学功能+补体与临床疾病
  • 【Linux网络-poll与epoll】epollserver设计(两个版本 Reactor)+epoll理论补充(LT ET)
  • DINOv2: Learning Robust Visual Features without Supervision
  • 1.第二阶段x64游戏实战-x86和x64的区别
  • 批量将 PDF 转换为 Word/PPT/Txt/Jpg图片等其它格式
  • Wireshark学习
  • 批量将多个彩色的 PDF 转换为黑白色
  • 2025年公路水运安全员 C 证考试内容及练习题
  • JS 防抖与节流
  • xr-frame 用cube代替线段实现两点间的连线
  • 采集类淘宝客网站怎么做/深圳优化公司找高粱seo服务
  • 网易企业邮箱价格表/优化营商环境个人心得
  • 湖北网站建设多少钱/网络营销包括哪些
  • 认真做门户网站迎检工作/百度seo排名优化提高流量
  • dede织梦php文章图片网站源码 完整后台 带在线音乐/网络维护培训班
  • 和县网站建设/网上打广告有哪些软件