Unity 基于UDP实现本地时间与网络时间校验 防客户端修改日期作弊

新建一个Unity GameObject 挂上NTPComponent脚本

在这里插入图片描述

时间校验

在这里插入图片描述

源码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using UnityEngine.Networking;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading.Tasks;
using System.Linq;

namespace GameContent
{
    /// <summary>
    /// 启动游戏后,将所有地址列表遍历
    /// </summary>
    [DisallowMultipleComponent]
    public class NTPComponent : MonoBehaviour
    {
        [Range( 5f, 60f )]
        public float CheckDuration = 5f;

        [Header( "NTP服务器域名列表" )]
        public List<string> NTPServerAddressList = new List<string>
        {
            "cn.pool.ntp.org",//国际NTP快速授时服务
            "ntp.ntsc.ac.cn",
            "pool.ntp.org" ,//全球通用
            "time1.google.com" ,//谷歌
            "time2.google.com",
            "time3.google.com",
            "time4.google.com",
            "time.apple.com" ,//苹果
            "time1.apple.com",
            "time2.apple.com",
            "time3.apple.com",
            "time.windows.com" ,//微软
            "time.nist.gov" ,//美国
            "cn.ntp.org.cn",//中国
            "stdtime.gov.hk",//香港
            "ntp.tencent.com",//腾讯云
            "ntp.aliyun.com",//阿里云
        };

        /// <summary>
        /// 网络时间是否生效中
        /// </summary>
        public bool IsValid { get; private set; }
        /// <summary>
        /// 当前Utc时间
        /// </summary>
        public DateTime NowUtc { get; private set; }
        
        [ReadOnly] public bool IsSyncState = false;
        //[SerializeField]
        private float mResidualCheckTime = 0f;

        private Socket mSocket = null;

        private void Start( )
        {
            mResidualCheckTime = CheckDuration;
            IsValid = false;
            NowUtc = DateTime.UtcNow;

            SearchNTPAddresses( );
        }

        #region NTP服务
        private void Update( )
        {
            if ( IsValid )
                NowUtc.AddSeconds( Time.unscaledDeltaTime );

            //没间隔n秒就同步一次utc时间
            mResidualCheckTime -= Time.unscaledDeltaTime;
            if ( mResidualCheckTime <= 0 )
            {
                mResidualCheckTime = CheckDuration;
                SearchNTPAddresses( );
            }
        }

        public async void SearchNTPAddresses( )
        {
            var tasks = NTPServerAddressList.Select( serverAddress => Task.Run( async ( ) => await GetNetworkUtcTimeAsync( serverAddress, 2000 ) ) ).ToArray( );

            while ( tasks.Length > 0 )
            {
                var completedTask = await Task.WhenAny( tasks );
                DateTime networkDateTime = completedTask.Result;
                if ( networkDateTime != DateTime.MinValue )
                {
                    bool oldState = IsValid;

                    IsValid = true;
                    NowUtc = completedTask.Result;
                    //Debug.Log( $"<Color=#FF0000>NTP = {NowUtc}</Color>" );

                    TimeSpan diff = NowUtc - DateTime.UtcNow;
                    IsSyncState = Mathf.Abs( ( float ) diff.TotalSeconds ) <= 10;

                    if ( oldState == false )
                        Fire.Event( GameEvent.ConnectNTPEvent, this );
                    return;
                }
                else
                {
                    tasks = tasks.Where( task => task != completedTask ).ToArray( );
                }
            }

            IsValid = false;
            //GameEntry.Event.Fire(this, ConnectNTPEventArgs.Create(false));
        }

        /// <summary>
        /// 异步获取时间 utc时间
        /// </summary>
        private async Task<DateTime> GetNetworkUtcTimeAsync( string ntpServer, int timeoutMilliseconds = 5000 )
        {
            try
            {
                const int udpPort = 123;
                var ntpData = new byte[ 48 ];
                ntpData[ 0 ] = 0x1B;

                var addresses = await Dns.GetHostAddressesAsync( ntpServer );
                var ipEndPoint = new IPEndPoint( addresses[ 0 ], udpPort );
                var socket = new Socket( AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp );

                // 设置超时时间
                socket.ReceiveTimeout = timeoutMilliseconds;

                await socket.ConnectAsync( ipEndPoint );
                await socket.SendAsync( new ArraySegment<byte>( ntpData ), SocketFlags.None );
                var receiveBuffer = new byte[ 48 ];
                await socket.ReceiveAsync( new ArraySegment<byte>( receiveBuffer ), SocketFlags.None );
                socket.Dispose( );

                const byte serverReplyTime = 40;
                ulong intPart = BitConverter.ToUInt32( receiveBuffer, serverReplyTime );
                ulong fractPart = BitConverter.ToUInt32( receiveBuffer, serverReplyTime + 4 );
                intPart = SwapEndianness( intPart );
                fractPart = SwapEndianness( fractPart );
                var milliseconds = ( intPart * 1000 ) + ( ( fractPart * 1000 ) / 0x100000000L );
                var networkUtcDateTime = new DateTime( 1900, 1, 1 ).AddMilliseconds( ( long ) milliseconds );

                //TimeZoneInfo serverTimeZone = TimeZoneInfo.Local; // 服务器的时区
                //var networkDateTime = TimeZoneInfo.ConvertTimeFromUtc(networkUtcDateTime, serverTimeZone);

                return networkUtcDateTime;
            }
            catch ( Exception ex )
            {
                // 出现异常,返回 null 或抛出错误,视情况而定
                //Debug.Log("获取网络时间失败: " + ex.Message);
                return DateTime.MinValue;
            }
        }

        // 交换字节顺序,将大端序转换为小端序或反之
        private uint SwapEndianness( ulong x )
        {
            return ( uint ) ( ( ( x & 0x000000ff ) << 24 ) +
                           ( ( x & 0x0000ff00 ) << 8 ) +
                           ( ( x & 0x00ff0000 ) >> 8 ) +
                           ( ( x & 0xff000000 ) >> 24 ) );
        }
        #endregion

    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Nodejs】基于Promise异步处理的博客demo代码实现

目录 package.json www.js db.js app.js routes/blog.js controllers/blog.js mysql.js responseModel.js 无开发&#xff0c;不安全。 这个demo项目实现了用Promise异步处理http的GET和POST请求&#xff0c;通过mysql的api实现了博客增删改查功能&#xff0c;但因没有…

vue3中pdf打印问题处理

1 get请求参数问题 之前的请求是post得不到参数&#xff0c;今天发现的问题很奇怪&#xff0c;从前端进入网关&#xff0c;网关居然得不到参数。 前端代码 const print () > {let linkUrlStr proxy.$tool.getUrlStr(proxy.$api.invOrder.psiInvOrder.printSalOutstock,{a…

Nougat:科学文档的OCR 使用记录

https://github.com/facebookresearch/nougat python环境需要在3.8以上 安装&#xff1a;pip install nougat-ocr 模型默认下载地址&#xff1a;/home/****/.cache/torch/hub/nougat-0.1.0-small 环境安装好之后默认使用cpu UserWarning: CUDA initialization: The NVIDIA dr…

基于决策树、随机森林和层次聚类对帕尔默企鹅数据分析

作者&#xff1a;i阿极 作者简介&#xff1a;数据分析领域优质创作者、多项比赛获奖者&#xff1a;博主个人首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞&#x1f44d;收藏&#x1f4c1;评论&#x1f4d2;关注哦&#x…

一文搞懂SiLM824x系列SiLM8243BBCL-DG 双通道死区可编程隔离驱动 主要特性与应用 让技术变得更有价值

SiLM824x系列SiLM8243BBCL-DG是一款具有不同配置的隔离双通道门极驱动器。SiLM8243BBCL-DG配置为高、低边驱动&#xff0c;SiLM8243BBCL-DG可提供4A的输出源电流和6A的灌电流能力&#xff0c;并且其驱动输出电压可以支持到33V。支持死区可编程&#xff0c;通过调整DT脚外部的电…

AI:106-基于卷积神经网络的遥感图像地物分类

🚀点击这里跳转到本专栏,可查阅专栏顶置最新的指南宝典~ 🎉🎊🎉 你的技术旅程将在这里启航! 从基础到实践,深入学习。无论你是初学者还是经验丰富的老手,对于本专栏案例和项目实践都有参考学习意义。 ✨✨✨ 每一个案例都附带有在本地跑过的关键代码,详细讲解供…

Sonarqube安装(Docker)

一&#xff0c;拉取相关镜像并运行 # 拉取sonarqube镜像 docker pull sonarqube:9.1.0-community在运行之前要提前安装postgres并允许&#xff0c;新建数据库名为sonar的数据库 Docker安装postgres教程 docker run -d --name sonarqube --restartalways \ -p 19000:9000 \ …

自制数据库空洞率清理工具-C版-02-EasyClean-V1.1(支持南大通用数据库Gbase8a)

一、环境信息 名称值CPUIntel(R) Core(TM) i5-1035G1 CPU 1.00GHz操作系统CentOS Linux release 7.9.2009 (Core)内存3G逻辑核数2Gbase8a版本8.6.2-R43.34.27468a27EasyClean版本V1.1 二、简述 工作和兴趣相结合的产物&#xff0c;既能更好的完成工作&#xff0c;也能看看自…

速学python·变量和类型

变量是什么 变量是在计算复杂程序过程中&#xff0c;用于保存中间结果的东西&#xff0c;这个东西一般是可变的量&#xff0c;也就是变量。 例如&#xff1a; 计算方差 1.计算平均值 2.计算每个数字与平均值的差值再平方 3.相加每一项平方值 4.除项数 Average (77.588.599.510…

公有云基本概念

1、租户和用户的关系 &#xff08;1&#xff09;租户是在华为云上注册的一个账号&#xff0c;登录之后&#xff0c;这个账号可以认为是一个“租户”。 &#xff08;2&#xff09;租户默认角色是一个“企业管理员”角色。拥有华为云上所有可以使用的功能。 &#xff08;3&…

C#:如何产生一个临时文件

在我们实际编程中&#xff0c;经常有将内容写到一个临时文件的需要。 比如&#xff1a;将网络上的图片下载下来&#xff0c;获取到图片的一些信息。 代码如下&#xff0c;看结果可知&#xff1a; 临时文件都是保存在系统临时文件夹的目录下&#xff0c;临时文件的扩展名统一…

机器学习(四) -- 模型评估(1)

系列文章目录 机器学习&#xff08;一&#xff09; -- 概述 机器学习&#xff08;二&#xff09; -- 数据预处理&#xff08;1-3&#xff09; 机器学习&#xff08;三&#xff09; -- 特征工程&#xff08;1-2&#xff09; 机器学习&#xff08;四&#xff09; -- 模型评估…

5.vue学习笔记(数组变化的侦测+计算属性+Class绑定)

文章目录 1.数组变化的侦测1.1.变更方法1.2.替换一个数组 2.计算属性计算属性缓存vs方法 3.Class绑定3.1.绑定对象3.2.多个对象的绑定形式3.3.绑定数组3.4.数组与对象 1.数组变化的侦测 1.1.变更方法 vue能够侦听响应式数组的变更方法&#xff0c;并在它们被调用时出发相关的…

在centos7部署redis7

一、目标 在centos7.9上部署安装redis-7.2.3 二、步骤 官网 https://redis.io/download/ 1、下载合适版本的redis wget https://github.com/redis/redis/archive/7.2.3.tar.gz --no-check-certificate 如果这个链接失效&#xff0c;自行去官网找个合适的版本 2、安装redi…

【CFP-专栏2】计算机类SCI优质期刊汇总(含IEEE/Top)

一、计算机区块链类SCI-IEEE 【期刊概况】IF:4.0-5.0, JCR2区&#xff0c;中科院2区&#xff1b; 【大类学科】计算机科学&#xff1b; 【检索情况】SCI在检&#xff1b; 【录用周期】3-5个月左右录用&#xff1b; 【截稿时间】12.31截稿&#xff1b; 【接收领域】区块链…

vue的小入门

vue的快速上手 Vue概念 是一个用于构建用户界面的渐进式框架优点&#xff1a;大大提高开发效率缺点&#xff1a;需要理解记忆规则 创建Vue实例 步骤&#xff1a; 准备容器引包创建Vue实例new Vue()指定配置项el data>渲染数据 el指定挂载点&#xff0c;选择器指定控制…

【REST2SQL】03 GO读取JSON文件

REST2SQL需要一些配置信息&#xff0c;用JSON文件保存&#xff0c;比如config.json 1 创建config.json配置文件 {"hostPort":"localhost:5217","connString":"oracle://blma:5217127.0.0.1:1521/CQYH","_oracle":"ora…

linux centos使用rz、sz命令上传下载文件

一般情况下&#xff0c;我们会使用终端软件&#xff0c;如 XShell、SecureCRT 或 FinalShell 来连接远程服务器后&#xff0c;使用 rz 命令上传本地文件到远程服务器&#xff0c;再解压发版上线。 一、安转使用 系统&#xff1a;Linux CentOS 7 安装 rz 和 sz 命令 yum -y ins…

Python 学习路线:介绍、基础语法、数据结构、算法、高级主题、框架及异步编程详解

Python 介绍 Python 是一种 高级 的、解释型 的、通用 的编程语言。其设计哲学强调代码的可读性&#xff0c;使用显著的缩进。Python 是 动态类型 和 垃圾收集 的。 基本语法 设置 Python 环境并开始基础知识。 变量 变量用于存储在计算机程序中引用和操作的信息。它们还提…

leetcode第206题反转链表❤

一&#xff1a;题目&#xff1a; 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1] 题目链接&#xff1a;力扣&#xff08;LeetCode&#xff09;官网…
最新文章