2009-07-28 12 views
85

Wszystko, czego potrzebuję, to sposób na wysłanie zapytania do serwera NTP za pomocą C#, aby uzyskać czas daty serwera NTP zwrócony jako string lub jako DateTime.Jak zapytać o serwer NTP za pomocą C#?

Jak to jest możliwe w najprostszej formie?

Odpowiedz

119

Ponieważ stary Akceptowane odpowiedź dostałem usunięte (To był link do Google wyników wyszukiwania kod, który już nie istnieje), pomyślałem, mogę odpowiedzieć na to pytanie dla przyszłości:

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

Uwaga: trzeba będzie dodać następujące przestrzenie nazw

using System.Net; 
using System.Net.Sockets; 
+0

To jest dobry kod i daje przynajmniej pojęcie o tym, która jest godzina ... ale kiedy wykonuję kod 5 razy w ciągu ułamka sekundy, czasy wynikowe różnią się od siebie na minutę ... więc coś jest tu zdecydowanie nie tak (bez względu na to, którego serwera czasu używam). –

+0

@MartinBookaWeser Czy używasz różnych wątków dla każdego połączenia? ponieważ 'Socket.Send' jest operacją synchroniczną (blokowanie). – Nasreddine

+0

nop, ten sam wątek, tylko dwie wywołania natychmiast po sobie nawzajem - powodują znacząco inne czasy ... –

9

The .NET Micro Framework Toolkit znaleźć w CodePlex ma NTPClient. Nigdy tego nie używałem, ale wygląda dobrze.

Istnieje również inny przykład lokalizacji here.

+0

http://www.programmersheaven.com /download/22219/2/ZipView.aspx nie znaleziono – Kiquenet

+0

Zestaw .NET Micro Framework Toolkit został przeniesiony na stronę [github.com/michaelschwarz/NETMF-Toolkit](https://github.com/michaelschwarz/NETMF-Toolkit) – stomy

30

to jest zoptymalizowana wersja funkcji, które usuwa zależność od funkcji BitConverter i sprawia, że ​​jest kompatybilny z NETMF (.NET Micro ramowej)

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

Brakuje limitu czasu ... 'socket.ReceiveTimeout = 3000;' ... to zapobiega zawieszaniu się, jeśli występuje problem z siecią. Wartość jest w milisekundach. –

+2

Zajmij się UTC: var networkDateTime = new DateTime (1900, 1, 1, 0, 0, 0, DateTimeKind.Utc) .AddMilliseconds (milliseconds); return networkDateTime.ToLocalTime(); –

-1

http://www.codeproject.com/Articles/237501/Windows-Phone-NTP-Client będzie działać dobrze dla systemu Windows Phone.

dodając odpowiedni kod

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

Zastosowanie:

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

'Socket' to' IDisposable'. Powinieneś zaprojektować klasę z myślą o tym i zapewnić sposób na uwolnienie gniazda zarówno podczas normalnego użytkowania, jak i przy każdym zgłoszeniu wyjątku. Ten kod powoduje przecieki pamięci – gfache

4

Wiem, że temat jest dość stary, ale takie narzędzia są zawsze pod ręką. Użyłem powyższych zasobów i stworzyłem wersję NtpClient, która pozwala asynchronicznie pozyskiwać dokładny czas zamiast zdarzenia.

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

Zastosowanie:

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

Nie działa w Windows 7, tylko w Windows 10. Zobacz [windows.networking.sockets.datagramsocket] (https://docs.microsoft.com/en-us/uwp/api/windows .networking.sockets.datagramsocket) – stomy

4

Zmodyfikowana wersja aby skompensować czas sieci i obliczyć z DateTime-kleszcze (dokładniej niż milisekund)

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 
} 
Powiązane problemy