C#を使用してNTPサーバーを照会する方法


104

必要なのは、C#を使用してNTPサーバーにクエリを送信し、NTPサーバーの日付時刻をa stringまたはとして返す方法だけDateTimeです。

最も簡単な形でこれはどのようにして可能ですか?

回答:


156

古い承認済みの回答が削除されたため(これは存在しない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;

2
@cvocvoのためにあなたが使用できることDateTime.ToLocalTime()
Nasreddine

2
NTPがブロックされている状況下では、このコードはハングして戻りません。このコードが確実に戻るように、タイムアウトなどを追加するにはどうすればよいですか?
cvocvo 2013年

4
これは、インターネットから直接プロダクションコードにカットアンドペーストするのに十分な数少ないコードの1つです(もちろんテストとレビューの後)。
dodgy_coder

2
LAN win7時間を取得する理由(コードを使用:)socket.Receive(ntpData);例外がスローされますAn existing connection was forcibly closed by the remote host。しかしnet time、時間を取得するためにコマンドラインを使用する場合は問題ありません。
qakmak 2015年

1
17行目:var socket = new Socket(AddressFamily.InterNetwork、SocketType.Dgram、ProtocolType.Udp); var socket = new Socket(addresses [0] .AddressFamily、SocketType.Dgram、ProtocolType.Udp);である必要があります。そうすれば、最初のアドレスがIP6であれば、それは機能します
Duane McKinney

33

これは、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;
}

7
タイムアウトがありませんか?socket.ReceiveTimeout = 3000;...これにより、ネットワークの問題が発生した場合にハングアップするのを防ぎます。値はミリ秒単位です。
dodgy_coder

3
UTCに注意してください:var networkDateTime = new DateTime(1900、1、1、0、0、0、DateTimeKind.Utc).AddMilliseconds(milliseconds); return networkDateTime.ToLocalTime();
Daniel Fisher lennybacon


6

トピックはかなり古いですが、そのようなツールは常に便利です。上記のリソースを使用して、イベントベースではなく非同期で正確な時刻を取得できるバージョンの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));
    }
}

使用法:

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

1
唯一のWindows 10を参照するには、Windows 7で動作しませんwindows.networking.sockets.datagramsocket
stomy

私はRaspberry Pi 3でこのinWind 10 IoT Coreプロジェクトを使用しましたが、piがオフになると時間が止まるので、ごちそうはうまくいきます。(リアルタイムクロックなし)
Chris Schaller

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
}

行var networkDateTime = new DateTime(TicksTo1900 + netTicks + pingTicks / 2);を変更することをお勧めします。変数によってnetworkDateTime = new DateTime(ticksTo1900 + netTicks + pingTicks / 2、DateTimeKind.Utc);
Bestter、2018年

こんにちはベスター、あなたのヒントをありがとう。.ToLocalTime()の場合、UTC種類は必須ではありません。オプションのUTCマーカー付きのタイムスタンプが必要な場合は、代わりに.ToUniversalTime()を呼び出すことができます。理由:DateTimeKindなしのコンストラクターを使用しました。これは、2〜3プロセッサーティックの方が速いためです。:D
MaxKlaxx 2018

-1

http://www.codeproject.com/Articles/237501/Windows-Phone-NTP-Clientは、Windows Phoneで適切に機能します。

関連するコードを追加する

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

使用法 :

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

SocketですIDisposable。これを念頭に置いてクラスを設計し、通常の使用時と例外が発生したときの両方でソケットを解放する方法を提供する必要があります。このコードはメモリリークの原因になります
gfache
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.