I’m writing an application in C# that needs to know if the system time is not fake (only if the network is on). For this reason, I make a class that retrieves the time from a NTP server. The class is written to prevent that the same servers are called too often using a simple circular array. For the data returned by the servers, I invite you to see the RFC-2030.
using System; using System.IO; using System.Runtime.Serialization; using System.Net; using System.Net.Sockets; public class NoServerFoundException : System.Exception { public NoServerFoundException() : base() { } public NoServerFoundException(string message) : base(message) { } public NoServerFoundException(string message, System.Exception inner) : base(message, inner) { } protected NoServerFoundException(SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { } } class NetworkTime { /* For more info, see: * NTP (RFC-2030) * http://tools.ietf.org/html/rfc2030 */ private const int requestTimeout = 3000; private const int timesForEachServer = 5; private const byte offTime = 40; //Transmit Time (see RFC-2030) private uint lastSrv; //NIST Servers public static string[] srvs = { "time.nist.gov", "pool.ntp.org", "europe.pool.ntp.org", "asia.pool.ntp.org", "oceania.pool.ntp.org", "north-america.pool.ntp.org", "south-america.pool.ntp.org", "africa.pool.ntp.org", "ntp1.inrim.it", "ntp2.inrim.it" }; public NetworkTime() { Random rnd = new Random(DateTime.Now.Millisecond); lastSrv = (uint)rnd.Next(0, srvs.Length); } private IPAddress getServer() { lastSrv = (uint)((lastSrv + 1) % srvs.Length); IPAddress[] address = Dns.GetHostEntry(srvs[lastSrv]).AddressList; if (address == null || address.Length == 0) throw new NoServerFoundException("no ip found"); return address[0]; } public DateTime GetDateTime() { return GetDateTime(false); } public DateTime GetDateTime(bool utc) { //Examine all servers until we find a server that responds for (int st = 0; st < srvs.Length * timesForEachServer; st++) { try { IPAddress ip = getServer(); IPEndPoint ipEndP = new IPEndPoint(ip, 123); Socket sk = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp); sk.ReceiveTimeout = requestTimeout; sk.Connect(ipEndP); /* Request * VN: 4 = NTP/SNTP version 4 * Mode: 3 = client */ byte[] data = new byte[48]; data[0] = 0x23; for (int i = 1; i < 48; i++) data[i] = 0; sk.Send(data); /* Response * we read the integer part and fraction part * of transmit time (see RFC-2030) */ sk.Receive(data); byte[] integerPart = new byte[4]; integerPart[0] = data[offTime + 3]; integerPart[1] = data[offTime + 2]; integerPart[2] = data[offTime + 1]; integerPart[3] = data[offTime + 0]; byte[] fractPart = new byte[4]; fractPart[0] = data[offTime + 7]; fractPart[1] = data[offTime + 6]; fractPart[2] = data[offTime + 5]; fractPart[3] = data[offTime + 4]; long ms = (long)( (ulong)BitConverter.ToUInt32(integerPart, 0) * 1000 + ((ulong)BitConverter.ToUInt32(fractPart, 0) * 1000) / 0x100000000L); sk.Close(); /* DateTime*/ DateTime date = new DateTime(1900, 1, 1); date += TimeSpan.FromTicks(ms * TimeSpan.TicksPerMillisecond); return utc ? date : date.ToLocalTime(); } catch (Exception ex) { } } throw new NoServerFoundException("no working server has been found"); } }
Download: NetworkTime.cs
3 responses to “Network Time Protocol request in C#”