Over the last week of so I've noticed several questions in the newsgroups about how to synchronize a CE device's time with a time server. Once again, it's not something I'd done, but I knew there were servers out there that provide the time publicly, so it couldn't bee too tough.
First stop was to see if Wikipedia gave a simple explanation. Useful, but not exactly what we need - we want to know exactly what the server expects, and what exactly it returns. It does give us the RFCs.
So a little looking at RFCs and we see that RFC 2030 is very applicable, and gives us all the info we need about the protocol.
A little more looking with Google found a public server domain name from NTP.org that actually rotates through public NTP servers, so we don't have to worry about one being up or not.
Armed with nothing but the second two links I wrote a little code. This could be far more robust, with fractional seconds. mode, stratum, precision info and all that, but I just wanted to get a reasonable time - so to the second is all I was after.
public DateTime GetNTPTime()
{
// 0x1B == 0b11011 == NTP version 3, client - see RFC 2030
byte[] ntpPacket = new byte[] { 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
IPAddress[] addressList = Dns.GetHostEntry("pool.ntp.org").AddressList;
if (addressList.Length == 0)
{
// error
return DateTime.MinValue;
}
IPEndPoint ep = new IPEndPoint(addressList[0], 123);
UdpClient client = new UdpClient();
client.Connect(ep);
client.Send(ntpPacket, ntpPacket.Length);
byte[] data = client.Receive(ref ep);
// receive date data is at offset 32
// Data is 64 bits - first 32 is seconds - we'll toss the fraction of a second
// it is not in an endian order, so we must rearrange
byte[] endianSeconds = new byte[4];
endianSeconds[0] = (byte)(data[32 + 3] & (byte)0x7F); // turn off MSB (some servers set it)
endianSeconds[1] = data[32 + 2];
endianSeconds[2] = data[32 + 1];
endianSeconds[3] = data[32 + 0];
uint seconds = BitConverter.ToUInt32(endianSeconds, 0);
return (new DateTime(1900, 1, 1)).AddSeconds(seconds);
}