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

我所需要的只是一种使用 C # 查询 NTP 服务器的方法,以获得作为 stringDateTime返回的 NTP 服务器的日期时间。

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

118330 次浏览

那个。在 CodePlex 中找到的 NET Micro Framework Toolkit 有一个 NTPClient。我自己从来没用过,不过看起来不错。

还有一个位于 给你的例子。

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

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;

这是该函数的优化版本,它消除了对 BitConverter 函数的依赖,并使其与 NETMF (兼容。NET 微框架)

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

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

我知道这个话题很老了,但是这样的工具总是很方便。我使用了上面的资源并创建了一个 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));

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