新建一个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
{
[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.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",
};
public bool IsValid { get; private set; }
public DateTime NowUtc { get; private set; }
[ReadOnly] public bool IsSyncState = false;
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 );
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;
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;
}
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 );
return networkUtcDateTime;
}
catch ( Exception ex )
{
return DateTime.MinValue;
}
}
private uint SwapEndianness( ulong x )
{
return ( uint ) ( ( ( x & 0x000000ff ) << 24 ) +
( ( x & 0x0000ff00 ) << 8 ) +
( ( x & 0x00ff0000 ) >> 8 ) +
( ( x & 0xff000000 ) >> 24 ) );
}
#endregion
}
}