Unity 基于UDP实现本地时间与网络时间校验 防客户端修改日期作弊
2024-01-07 17:24:13
新建一个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
}
}
文章来源:https://blog.csdn.net/qq_39162566/article/details/135371137
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!