131 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C#
		
	
	
	
			
		
		
	
	
			131 lines
		
	
	
		
			5.0 KiB
		
	
	
	
		
			C#
		
	
	
	
| using System;
 | |
| using System.Net;
 | |
| using System.Net.Sockets;
 | |
| using System.Linq;
 | |
| 
 | |
| namespace ThinkingSDK.PC.Time
 | |
| {
 | |
|     public class ThinkingSDKNTPCalibration : ThinkingSDKTimeCalibration
 | |
|     {
 | |
|         public ThinkingSDKNTPCalibration(string ntpServer) {
 | |
|             double totalMilliseconds = ConvertDateTimeInt(DateTime.UtcNow);
 | |
|             this.mStartTime = (long)totalMilliseconds;
 | |
|             this.mSystemElapsedRealtime = Environment.TickCount;
 | |
| 
 | |
|             // request scoket time
 | |
|             Socket socket = GetNetworkTimeSync(ntpServer, this);
 | |
|             // set scoket timeout
 | |
|             TDTimeout.SetTimeout(3, new Action<object>(ScoketTimeout), (object)socket);
 | |
|         }
 | |
| 
 | |
|         private void ScoketTimeout(object obj)
 | |
|         {
 | |
|             if (obj is Socket)
 | |
|             {
 | |
|                 Socket socket = (Socket)obj;
 | |
|                 if (socket.Connected == true)
 | |
|                 {
 | |
|                     socket.Close();
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         protected static new double ConvertDateTimeInt(System.DateTime time)
 | |
|         {
 | |
|             DateTime startTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
 | |
|  		    return (double)(time - startTime).TotalMilliseconds;
 | |
|  		}
 | |
| 
 | |
|         private static Socket GetNetworkTimeSync(string ntpServer, ThinkingSDKTimeCalibration timeCalibration)
 | |
|         {
 | |
|             // NTP message size - 16 bytes of the digest (RFC 2030)
 | |
|             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 addressFirst = addresses.First(e => e.AddressFamily == AddressFamily.InterNetwork);
 | |
|             if (addressFirst == null)
 | |
|             {
 | |
|                 addressFirst = addresses[0];
 | |
|             }
 | |
| 
 | |
|             //The UDP port number assigned to NTP is 123
 | |
|             var ipEndPoint = new IPEndPoint(addressFirst, 123);
 | |
|             //NTP uses UDP
 | |
|             var socket = new Socket(ipEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
 | |
| 
 | |
|             socket.Connect(ipEndPoint);
 | |
| 
 | |
|             SocketAsyncEventArgs socketAsyncEventArgs = new SocketAsyncEventArgs();
 | |
|             socketAsyncEventArgs.SetBuffer(ntpData, 0, ntpData.Length);
 | |
|             socketAsyncEventArgs.UserToken = timeCalibration;
 | |
|             socketAsyncEventArgs.RemoteEndPoint = ipEndPoint;
 | |
|             socketAsyncEventArgs.Completed += SocketAsyncEventArgs_Completed;
 | |
|             // send socket request
 | |
|             socket.SendAsync(socketAsyncEventArgs);
 | |
| 
 | |
|             return socket;
 | |
|         }
 | |
| 
 | |
|         private static void SocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs eventArgs)
 | |
|         {
 | |
|             Socket socket = (Socket)sender;
 | |
|             if (eventArgs.SocketError == SocketError.Success)
 | |
|             {
 | |
|                 if (eventArgs.LastOperation == SocketAsyncOperation.Send)
 | |
|                 {
 | |
|                     socket.ReceiveAsync(eventArgs);
 | |
|                 }
 | |
|                 else if (eventArgs.LastOperation == SocketAsyncOperation.Receive)
 | |
|                 {
 | |
|                     if (eventArgs.SocketError == SocketError.Success && eventArgs.Buffer.Length > 0)
 | |
|                     {
 | |
|                         DateTime ntpTime = ParseDateTimeWithNTPData(eventArgs.Buffer);
 | |
|                         double totalMilliseconds = ConvertDateTimeInt(ntpTime);
 | |
|                         ThinkingSDKTimeCalibration timeCalibration = (ThinkingSDKTimeCalibration)eventArgs.UserToken;
 | |
|                         timeCalibration.mStartTime = (long)totalMilliseconds;
 | |
|                     }
 | |
|                     socket.Close();
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     socket.Close();
 | |
|                 }
 | |
|             }
 | |
|             else
 | |
|             {
 | |
|                 socket.Close();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         static uint SwapEndianness(ulong x)
 | |
| 		{
 | |
| 			return (uint)(((x & 0x000000ff) << 24) + ((x & 0x0000ff00) << 8) + ((x & 0x00ff0000) >> 8) + ((x & 0xff000000) >> 24));
 | |
| 		}
 | |
| 
 | |
|         private static DateTime ParseDateTimeWithNTPData(byte[] ntpData)
 | |
|         {
 | |
|             //Offset to get to the "Transmit Timestamp" field (time at which the reply 
 | |
|             //departed the server for the client, in 64-bit timestamp format."
 | |
|             const byte serverReplyTime = 40;
 | |
| 
 | |
|             //Get the seconds part
 | |
|             ulong intPart = BitConverter.ToUInt32(ntpData, serverReplyTime);
 | |
| 
 | |
|             //Get the seconds fraction
 | |
|             ulong fractPart = BitConverter.ToUInt32(ntpData, serverReplyTime + 4);
 | |
| 
 | |
|             //Convert From big-endian to little-endian
 | |
|             intPart = SwapEndianness(intPart);
 | |
|             fractPart = SwapEndianness(fractPart);
 | |
| 
 | |
|             var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
 | |
| 
 | |
|             //**UTC** time
 | |
|             var networkDateTime = (new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds((long)milliseconds);
 | |
|             return networkDateTime;
 | |
|         }
 | |
|     }
 | |
| } |