127 lines
3.9 KiB
C#
127 lines
3.9 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Events;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Net;
|
|
using System.Net.Sockets;
|
|
using System.Threading;
|
|
|
|
// NTP同期時刻を返却するクラス
|
|
public class NtpRequest {
|
|
|
|
private DateTime ntpDateTime;
|
|
private Thread thread;
|
|
private Socket socket;
|
|
private volatile bool threadRunning = false;
|
|
private int timeout = 3000; // ms
|
|
|
|
private int retryCount = 0;
|
|
|
|
private string[] NTP_SERVER = {
|
|
"time.google.com",
|
|
"cn.pool.ntp.org",
|
|
};
|
|
|
|
void OnApplicationQuit() {
|
|
CloseRequest ();
|
|
}
|
|
|
|
public Coroutine GetNetworkTime(MonoBehaviour behaviour, Action<DateTime> succeededCallback, Action failedCallback, int timeout = 3000)
|
|
{
|
|
if (threadRunning) return null;
|
|
|
|
// リクエスト実行
|
|
retryCount = 0;
|
|
this.timeout = timeout;
|
|
ntpDateTime = DateTime.MinValue;
|
|
_threadStart();
|
|
return behaviour.StartCoroutine(WaitForRequest(succeededCallback, failedCallback));
|
|
}
|
|
private void _threadStart()
|
|
{
|
|
threadRunning = true;
|
|
thread = new Thread(new ThreadStart(NetworkRequest));
|
|
thread.Start();
|
|
}
|
|
|
|
private IEnumerator WaitForRequest(Action<DateTime> succeededCallback, Action failedCallback)
|
|
{
|
|
int startTickCount = Environment.TickCount;
|
|
bool isTimeouted = false;
|
|
while ( threadRunning ) {
|
|
if (ntpDateTime > DateTime.MinValue){
|
|
break;
|
|
}
|
|
// タイムアウト
|
|
else if (Environment.TickCount - startTickCount > timeout) {
|
|
isTimeouted = true;
|
|
break;
|
|
}
|
|
yield return 0;
|
|
}
|
|
|
|
CloseRequest ();
|
|
threadRunning = false;
|
|
|
|
if(!isTimeouted){
|
|
if(succeededCallback != null)
|
|
succeededCallback (ntpDateTime);
|
|
// リトライ
|
|
}else if(retryCount < NTP_SERVER.Length-1){
|
|
retryCount++;
|
|
isTimeouted = false;
|
|
_threadStart();
|
|
yield return WaitForRequest(succeededCallback, failedCallback);
|
|
}else{
|
|
failedCallback();
|
|
}
|
|
}
|
|
|
|
private void NetworkRequest()
|
|
{
|
|
try{
|
|
string ntpServer = NTP_SERVER[retryCount];
|
|
|
|
var ntpData = new byte[48];
|
|
//Setting the Leap Indicator, Version Number and Mode values
|
|
ntpData[0] = 0x1B; //LI = 0 (no warning), VN = 3 (IPv4 only), Mode = 3 (Client Mode)
|
|
|
|
var addresses = Dns.GetHostEntry(ntpServer).AddressList;
|
|
var ipEndPoint = new IPEndPoint(addresses[0], 123);
|
|
|
|
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
|
|
|
|
// ソケット通信開始
|
|
socket.Connect(ipEndPoint);
|
|
socket.Send(ntpData);
|
|
socket.Receive(ntpData);
|
|
socket.Close();
|
|
|
|
ulong intPart = (ulong)ntpData[40] << 24 | (ulong)ntpData[41] << 16 | (ulong)ntpData[42] << 8 | (ulong)ntpData[43];
|
|
ulong fractPart = (ulong)ntpData[44] << 24 | (ulong)ntpData[45] << 16 | (ulong)ntpData[46] << 8 | (ulong)ntpData[47];
|
|
|
|
var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
|
|
var networkDateTime = (new DateTime(1900, 1, 1)).AddMilliseconds((long)milliseconds);
|
|
|
|
ntpDateTime = networkDateTime;
|
|
|
|
}catch(SocketException){
|
|
// TODO 例外フィルタでSocketException: Access deniedのみ取りたいが、C#のバージョンが足りない
|
|
// https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/try-catch
|
|
}catch(ThreadAbortException){
|
|
}
|
|
}
|
|
|
|
private void CloseRequest(){
|
|
if ( thread != null ) {
|
|
thread.Abort();
|
|
}
|
|
if ( socket != null ) {
|
|
socket.Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|