How to Query an NTP Server using C#?

前端 未结 6 2021
一生所求
一生所求 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 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.

     /// 
    /// Represents a client which can obtain accurate time via NTP protocol.
    /// 
    public class NtpClient
    {
        private readonly TaskCompletionSource _resultCompletionSource;
    
        /// 
        /// Creates a new instance of  class.
        /// 
        public NtpClient()
        {
            _resultCompletionSource = new TaskCompletionSource();
        }
    
        /// 
        /// Gets accurate time using the NTP protocol with default timeout of 45 seconds.
        /// 
        /// Network accurate  value.
        public async Task GetNetworkTimeAsync()
        {
            return await GetNetworkTimeAsync(TimeSpan.FromSeconds(45));
        }
    
        /// 
        /// Gets accurate time using the NTP protocol with default timeout of 45 seconds.
        /// 
        /// Operation timeout in milliseconds.
        /// Network accurate  value.
        public async Task GetNetworkTimeAsync(int timeoutMs)
        {
            return await GetNetworkTimeAsync(TimeSpan.FromMilliseconds(timeoutMs));
        }
    
        /// 
        /// Gets accurate time using the NTP protocol with default timeout of 45 seconds.
        /// 
        /// Operation timeout.
        /// Network accurate  value.
        public async Task 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));
    

提交回复
热议问题