新建一个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 }
}