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