How to Query an NTP Server using C#?

前端 未结 6 1965
一生所求
一生所求 2020-11-22 03:09

All I need is a way to query an NTP Server using C# to get the Date Time of the NTP Server returned as either a string or as a DateTime.

H

相关标签:
6条回答
  • 2020-11-22 03:49

    http://www.codeproject.com/Articles/237501/Windows-Phone-NTP-Client is going to work well for Windows Phone .

    Adding the relevant code

    /// <summary>
    /// Class for acquiring time via Ntp. Useful for applications in which correct world time must be used and the 
    /// clock on the device isn't "trusted."
    /// </summary>
    public class NtpClient
    {
        /// <summary>
        /// Contains the time returned from the Ntp request
        /// </summary>
        public class TimeReceivedEventArgs : EventArgs
        {
            public DateTime CurrentTime { get; internal set; }
        }
    
        /// <summary>
        /// Subscribe to this event to receive the time acquired by the NTP requests
        /// </summary>
        public event EventHandler<TimeReceivedEventArgs> TimeReceived;
    
        protected void OnTimeReceived(DateTime time)
        {
            if (TimeReceived != null)
            {
                TimeReceived(this, new TimeReceivedEventArgs() { CurrentTime = time });
            }
        }
    
    
        /// <summary>
        /// Not reallu used. I put this here so that I had a list of other NTP servers that could be used. I'll integrate this
        /// information later and will provide method to allow some one to choose an NTP server.
        /// </summary>
        public string[] NtpServerList = new string[]
        {
            "pool.ntp.org ",
            "asia.pool.ntp.org",
            "europe.pool.ntp.org",
            "north-america.pool.ntp.org",
            "oceania.pool.ntp.org",
            "south-america.pool.ntp.org",
            "time-a.nist.gov"
        };
    
        string _serverName;
        private Socket _socket;
    
        /// <summary>
        /// Constructor allowing an NTP server to be specified
        /// </summary>
        /// <param name="serverName">the name of the NTP server to be used</param>
        public NtpClient(string serverName)
        {
            _serverName = serverName;
        }
    
    
        /// <summary>
        /// 
        /// </summary>
        public NtpClient()
            : this("time-a.nist.gov")
        { }
    
        /// <summary>
        /// Begins the network communication required to retrieve the time from the NTP server
        /// </summary>
        public void RequestTime()
        {
            byte[] buffer = new byte[48];
            buffer[0] = 0x1B;
            for (var i = 1; i < buffer.Length; ++i)
                buffer[i] = 0;
            DnsEndPoint _endPoint = new DnsEndPoint(_serverName, 123);
    
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            SocketAsyncEventArgs sArgsConnect = new SocketAsyncEventArgs() { RemoteEndPoint = _endPoint };
            sArgsConnect.Completed += (o, e) =>
            {
                if (e.SocketError == SocketError.Success)
                {
                    SocketAsyncEventArgs sArgs = new SocketAsyncEventArgs() { RemoteEndPoint = _endPoint };
                    sArgs.Completed +=
                        new EventHandler<SocketAsyncEventArgs>(sArgs_Completed);
                    sArgs.SetBuffer(buffer, 0, buffer.Length);
                    sArgs.UserToken = buffer;
                    _socket.SendAsync(sArgs);
                }
            };
            _socket.ConnectAsync(sArgsConnect);
    
        }
    
        void sArgs_Completed(object sender, SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                byte[] buffer = (byte[])e.Buffer;
                SocketAsyncEventArgs sArgs = new SocketAsyncEventArgs();
                sArgs.RemoteEndPoint = e.RemoteEndPoint;
    
                sArgs.SetBuffer(buffer, 0, buffer.Length);
                sArgs.Completed += (o, a) =>
                {
                    if (a.SocketError == SocketError.Success)
                    {
                        byte[] timeData = a.Buffer;
    
                        ulong hTime = 0;
                        ulong lTime = 0;
    
                        for (var i = 40; i <= 43; ++i)
                            hTime = hTime << 8 | buffer[i];
                        for (var i = 44; i <= 47; ++i)
                            lTime = lTime << 8 | buffer[i];
                        ulong milliseconds = (hTime * 1000 + (lTime * 1000) / 0x100000000L);
    
                        TimeSpan timeSpan =
                            TimeSpan.FromTicks((long)milliseconds * TimeSpan.TicksPerMillisecond);
                        var currentTime = new DateTime(1900, 1, 1) + timeSpan;
                        OnTimeReceived(currentTime);
    
                    }
                };
                _socket.ReceiveAsync(sArgs);
            }
        }
    }
    

    Usage :

    public partial class MainPage : PhoneApplicationPage
    {
        private NtpClient _ntpClient;
        public MainPage()
        {
            InitializeComponent();
            _ntpClient = new NtpClient();
            _ntpClient.TimeReceived += new EventHandler<NtpClient.TimeReceivedEventArgs>(_ntpClient_TimeReceived);
        }
    
        void _ntpClient_TimeReceived(object sender, NtpClient.TimeReceivedEventArgs e)
        {
            this.Dispatcher.BeginInvoke(() =>
                                            {
                                                txtCurrentTime.Text = e.CurrentTime.ToLongTimeString();
                                                txtSystemTime.Text = DateTime.Now.ToUniversalTime().ToLongTimeString();
                                            });
        }
    
        private void UpdateTimeButton_Click(object sender, RoutedEventArgs e)
        {
            _ntpClient.RequestTime();
        }
    }
    
    0 讨论(0)
  • 2020-11-22 03:54

    Since the old accepted answer got deleted (It was a link to a Google code search results that no longer exist), I figured I could answer this question for future reference :

    public static DateTime GetNetworkTime()
    {
        //default Windows time server
        const string ntpServer = "time.windows.com";
    
        // 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;
    
        //The UDP port number assigned to NTP is 123
        var ipEndPoint = new IPEndPoint(addresses[0], 123);
        //NTP uses UDP
    
        using(var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
        {
            socket.Connect(ipEndPoint);
    
            //Stops code hang if NTP is blocked
            socket.ReceiveTimeout = 3000;     
    
            socket.Send(ntpData);
            socket.Receive(ntpData);
            socket.Close();
        }
    
        //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.ToLocalTime();
    }
    
    // stackoverflow.com/a/3294698/162671
    static uint SwapEndianness(ulong x)
    {
        return (uint) (((x & 0x000000ff) << 24) +
                       ((x & 0x0000ff00) << 8) +
                       ((x & 0x00ff0000) >> 8) +
                       ((x & 0xff000000) >> 24));
    }
    

    Note: You will have to add the following namespaces

    using System.Net;
    using System.Net.Sockets;
    
    0 讨论(0)
  • 2020-11-22 04:03

    I know the topic is quite old, but such tools are always handy. I've used the resources above and created a version of NtpClient which allows asynchronously to acquire accurate time, instead of event based.

     /// <summary>
    /// Represents a client which can obtain accurate time via NTP protocol.
    /// </summary>
    public class NtpClient
    {
        private readonly TaskCompletionSource<DateTime> _resultCompletionSource;
    
        /// <summary>
        /// Creates a new instance of <see cref="NtpClient"/> class.
        /// </summary>
        public NtpClient()
        {
            _resultCompletionSource = new TaskCompletionSource<DateTime>();
        }
    
        /// <summary>
        /// Gets accurate time using the NTP protocol with default timeout of 45 seconds.
        /// </summary>
        /// <returns>Network accurate <see cref="DateTime"/> value.</returns>
        public async Task<DateTime> GetNetworkTimeAsync()
        {
            return await GetNetworkTimeAsync(TimeSpan.FromSeconds(45));
        }
    
        /// <summary>
        /// Gets accurate time using the NTP protocol with default timeout of 45 seconds.
        /// </summary>
        /// <param name="timeoutMs">Operation timeout in milliseconds.</param>
        /// <returns>Network accurate <see cref="DateTime"/> value.</returns>
        public async Task<DateTime> GetNetworkTimeAsync(int timeoutMs)
        {
            return await GetNetworkTimeAsync(TimeSpan.FromMilliseconds(timeoutMs));
        }
    
        /// <summary>
        /// Gets accurate time using the NTP protocol with default timeout of 45 seconds.
        /// </summary>
        /// <param name="timeout">Operation timeout.</param>
        /// <returns>Network accurate <see cref="DateTime"/> value.</returns>
        public async Task<DateTime> GetNetworkTimeAsync(TimeSpan timeout)
        {
            using (var socket = new DatagramSocket())
            using (var ct = new CancellationTokenSource(timeout))
            {
                ct.Token.Register(() => _resultCompletionSource.TrySetCanceled());
    
                socket.MessageReceived += OnSocketMessageReceived;
                //The UDP port number assigned to NTP is 123
                await socket.ConnectAsync(new HostName("pool.ntp.org"), "123");
                using (var writer = new DataWriter(socket.OutputStream))
                {
                    // NTP message size is 16 bytes of the digest (RFC 2030)
                    var ntpBuffer = new byte[48];
    
                    // Setting the Leap Indicator, 
                    // Version Number and Mode values
                    // LI = 0 (no warning)
                    // VN = 3 (IPv4 only)
                    // Mode = 3 (Client Mode)
                    ntpBuffer[0] = 0x1B;
    
                    writer.WriteBytes(ntpBuffer);
                    await writer.StoreAsync();
                    var result = await _resultCompletionSource.Task;
                    return result;
                }
            }
        }
    
        private void OnSocketMessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
        {
            try
            {
                using (var reader = args.GetDataReader())
                {
                    byte[] response = new byte[48];
                    reader.ReadBytes(response);
                    _resultCompletionSource.TrySetResult(ParseNetworkTime(response));
                }
            }
            catch (Exception ex)
            {
                _resultCompletionSource.TrySetException(ex);
            }
        }
    
        private static DateTime ParseNetworkTime(byte[] rawData)
        {
            //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(rawData, serverReplyTime);
    
            //Get the seconds fraction
            ulong fractPart = BitConverter.ToUInt32(rawData, serverReplyTime + 4);
    
            //Convert From big-endian to little-endian
            intPart = SwapEndianness(intPart);
            fractPart = SwapEndianness(fractPart);
    
            var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L);
    
            //**UTC** time
            DateTime networkDateTime = (new DateTime(1900, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc)).AddMilliseconds((long)milliseconds);
            return networkDateTime;
        }
    
        // stackoverflow.com/a/3294698/162671
        private static uint SwapEndianness(ulong x)
        {
            return (uint)(((x & 0x000000ff) << 24) +
                           ((x & 0x0000ff00) << 8) +
                           ((x & 0x00ff0000) >> 8) +
                           ((x & 0xff000000) >> 24));
        }
    }
    

    Usage:

    var ntp = new NtpClient();
    var accurateTime = await ntp.GetNetworkTimeAsync(TimeSpan.FromSeconds(10));
    
    0 讨论(0)
  • 2020-11-22 04:09

    The .NET Micro Framework Toolkit found in the CodePlex has an NTPClient. I have never used it myself but it looks good.

    There is also another example located here.

    0 讨论(0)
  • 2020-11-22 04:11

    A modified version to compensate network times and calculate with DateTime-Ticks (more precise than milliseconds)

    public static DateTime GetNetworkTime()
    {
      const string NtpServer = "pool.ntp.org";
    
      const int DaysTo1900 = 1900 * 365 + 95; // 95 = offset for leap-years etc.
      const long TicksPerSecond = 10000000L;
      const long TicksPerDay = 24 * 60 * 60 * TicksPerSecond;
      const long TicksTo1900 = DaysTo1900 * TicksPerDay;
    
      var ntpData = new byte[48];
      ntpData[0] = 0x1B; // LeapIndicator = 0 (no warning), VersionNum = 3 (IPv4 only), Mode = 3 (Client Mode)
    
      var addresses = Dns.GetHostEntry(NtpServer).AddressList;
      var ipEndPoint = new IPEndPoint(addresses[0], 123);
      long pingDuration = Stopwatch.GetTimestamp(); // temp access (JIT-Compiler need some time at first call)
      using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
      {
        socket.Connect(ipEndPoint);
        socket.ReceiveTimeout = 5000;
        socket.Send(ntpData);
        pingDuration = Stopwatch.GetTimestamp(); // after Send-Method to reduce WinSocket API-Call time
    
        socket.Receive(ntpData);
        pingDuration = Stopwatch.GetTimestamp() - pingDuration;
      }
    
      long pingTicks = pingDuration * TicksPerSecond / Stopwatch.Frequency;
    
      // optional: display response-time
      // Console.WriteLine("{0:N2} ms", new TimeSpan(pingTicks).TotalMilliseconds);
    
      long intPart = (long)ntpData[40] << 24 | (long)ntpData[41] << 16 | (long)ntpData[42] << 8 | ntpData[43];
      long fractPart = (long)ntpData[44] << 24 | (long)ntpData[45] << 16 | (long)ntpData[46] << 8 | ntpData[47];
      long netTicks = intPart * TicksPerSecond + (fractPart * TicksPerSecond >> 32);
    
      var networkDateTime = new DateTime(TicksTo1900 + netTicks + pingTicks / 2);
    
      return networkDateTime.ToLocalTime(); // without ToLocalTime() = faster
    }
    
    0 讨论(0)
  • 2020-11-22 04:12

    This is a optimized version of the function which removes dependency on BitConverter function and makes it compatible with NETMF (.NET Micro Framework)

    public static DateTime GetNetworkTime()
    {
        const string ntpServer = "pool.ntp.org";
        var ntpData = new byte[48];
        ntpData[0] = 0x1B; //LeapIndicator = 0 (no warning), VersionNum = 3 (IPv4 only), Mode = 3 (Client Mode)
    
        var addresses = Dns.GetHostEntry(ntpServer).AddressList;
        var ipEndPoint = new IPEndPoint(addresses[0], 123);
        var 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);
    
        return networkDateTime;
    }
    
    0 讨论(0)
提交回复
热议问题