Querying an NTP/SNTP Time Server

    UPDATE 4 Sept 2013

The code snippet below has been fixed. It used to have a bug in it where it would occasionally be 2 minutes off of the correct time. I also added in parsing the fraction of a second.

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()
{
    IPAddress[] addressList;

    try
    {
        addressList = Dns.GetHostEntry(m_server).AddressList;

        if (addressList.Length == 0)
        {
            // error
            return DateTime.MinValue;
        }
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex.Message);
        if (Debugger.IsAttached) Debugger.Break();

        return DateTime.MinValue;
    }


    lock (this)
    {
        IPEndPoint ep = new IPEndPoint(addressList[0], 123);
        UdpClient client = new UdpClient();
        client.Connect(ep);
        client.Send(m_ntpPacket, m_ntpPacket.Length);
        byte[] data = client.Receive(ref ep);

        // receive date data is at offset 32
        // Data is 64 bits - first 32 is seconds
        // it is not in an endian order, so we must rearrange
        byte[] endianSeconds = new byte[4];
        endianSeconds[0] = data[32 + 3];
        endianSeconds[1] = data[32 + 2];
        endianSeconds[2] = data[32 + 1];
        endianSeconds[3] = data[32 + 0];
        uint seconds = BitConverter.ToUInt32(endianSeconds, 0);

        // second 32 is fraction of a second
        endianSeconds[0] = data[32 + 7];
        endianSeconds[1] = data[32 + 6];
        endianSeconds[2] = data[32 + 5];
        endianSeconds[3] = data[32 + 4];

        uint fraction = BitConverter.ToUInt32(endianSeconds, 0);

        var s = double.Parse(string.Format("{0}.{1}", seconds, fraction));

        return (new DateTime(1900, 1, 1)).AddSeconds(s);
    }
}

5 thoughts on “Querying an NTP/SNTP Time Server”

  1. Have you encountered a situation where a device in a cradle cannot get the NTP packet back? The DNS lookup works, but the call to the client.Receive(ref ep) never returns anything. When I take the WM device off the cradle and use it’s WiFi radio, the time comes back very quickly.

    Thanks,
    -MrB

    Like

  2. test to see whether or not you are wireless like this:

    bool Wireless = false;
    try
    {
    System.Net.Dns.GetHostEntry("PPP_PEER");
    Wireless = false;
    }
    catch (Exception)
    {
    Wireless = true;
    }

    Like

  3. The timeout problem has something to do with the active sync internet connection. It seems the connection ist not able to get back the udp answer packet.

    My solution is a UdpClient which inherts System.Net.Sockets.UdpClient and implements a Receive-Method which has a timeout flag. See the code below.

    Namespace Bertel.Net.Sockets

    Public Class UdpClient
    Inherits System.Net.Sockets.UdpClient

    Private Class SocketState
    Public Socket As System.Net.Sockets.Socket
    Public Buffer As Byte()
    Public BytesRead As Integer
    Public WaitHandle As System.Threading.ManualResetEvent
    Public Sub New()
    Socket = Nothing
    Buffer = Nothing
    BytesRead = 0
    WaitHandle = New System.Threading.ManualResetEvent(False)
    End Sub
    End Class

    Private Shared Sub ReceiveCallback(ByVal ar As IAsyncResult)
    Dim state As SocketState = CType(ar.AsyncState, SocketState)
    Try
    state.BytesRead = state.Socket.EndReceive(ar)
    Catch ex As ObjectDisposedException
    End Try
    state.WaitHandle.Set()
    End Sub

    Public Overloads Function Receive(ByVal buffer As Byte(), ByVal timeout As Integer) As Integer
    Dim state As SocketState, callback As System.AsyncCallback, result As System.IAsyncResult
    state = New SocketState
    state.Socket = Client
    state.Buffer = buffer
    callback = New System.AsyncCallback(AddressOf UdpClient.ReceiveCallback)
    result = state.Socket.BeginReceive(state.Buffer, 0, state.Buffer.Length, _
    System.Net.Sockets.SocketFlags.None, callback, state)
    If state.WaitHandle.WaitOne(timeout, False) Then
    Return state.BytesRead
    Else
    Return -1
    End If
    End Function

    ‘ Public Class UdpClient
    End Class

    ‘ Namespace Bertel.Net.Sockets
    End Namespace

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s