如何使用 C# 查询 NTP 服务器?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1193955/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 10:31:30  来源:igfitidea点击:

How to Query an NTP Server using C#?

c#datetimentp

提问by JL.

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 stringor as a DateTime.

我所需要的只是一种使用 C# 查询 NTP 服务器的方法,以获取 NTP 服务器的日期时间作为 astringDateTime.

How is this possible in its simplest form?

这怎么可能以最简单的形式出现?

采纳答案by Nasreddine

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;

回答by Tony Borf

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

CodePlex 中的 .NET Micro Framework Toolkit具有NTPClient. 我自己从未使用过它,但它看起来不错。

There is also another example located here.

还有另一个示例位于此处

回答by GonzaloG

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

这是该函数的优化版本,它消除了对 BitConverter 函数的依赖并使其与 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;
}

回答by Bageshwar Pratap Narain

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

http://www.codeproject.com/Articles/237501/Windows-Phone-NTP-Client将适用于 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();
    }
}

回答by Momchil Marinov

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.

我知道这个话题已经很老了,但这些工具总是很方便。我已经使用了上面的资源并创建了一个 NtpClient 版本,它允许异步获取准确的时间,而不是基于事件。

 /// <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));

回答by MaxKlaxx

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

用于补偿网络时间并使用 DateTime-Ticks 进行计算的修改版本(比毫秒更精确)

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
}