当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何使用C#查询NTP服务器?》经验,为你挑选了5个好方法。

我只需要一种使用C#查询NTP服务器的方法,以便将NTP服务器的日期时间作为a string或a 返回DateTime.

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



1> Nasreddine..:

由于旧的已接受的答案被删除(这是一个不再存在的Google代码搜索结果的链接),我想我可以回答这个问题以供将来参考:

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

注意:您必须添加以下命名空间

using System.Net;
using System.Net.Sockets;


这是为数不多的代码之一,可以直接从Internet上剪切并粘贴到生产代码中(经过测试和审查).
@cvocvo为此您可以使用[`DateTime.ToLocalTime()`](http://msdn.microsoft.com/en-us/library/system.datetime.tolocaltime.aspx)
在NTP被阻止的情况下,此代码挂起并且永不返回.如何添加超时或确保此代码返回的内容?
为什么我要获得LAN win7时间(使用代码位置:`socket.Receive(ntpData);`),它将引发异常:`现有连接被远程主机强行关闭。但是,如果我使用命令行`net time`来获取时间,那一切都很好。

2> 小智..:

这是该函数的优化版本,它消除了对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;
}


你是否错过了超时...`socket.ReceiveTimeout = 3000;`...如果出现网络问题,这可以防止它挂起.值以毫秒为单位.
照顾UTC:var networkDateTime = new DateTime(1900,1,1,0,0,0,DateTimeKind.Utc).AddMilliseconds(毫秒); return networkDateTime.ToLocalTime();

3> Tony Borf..:

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

还有位于另一个例子在这里.



4> Momchil Mari..:

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

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

用法:

var ntp = new NtpClient();
var accurateTime = await ntp.GetNetworkTimeAsync(TimeSpan.FromSeconds(10));



5> 小智..:

修改版本以补偿网络时间并使用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
}

推荐阅读
TXCWB_523
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有