rdavisau / sockets-for-pcl

Cross-platform socket API for Xamarin iOS/Android/Forms, Xamarin.Mac/MonoMac, Windows Phone 8/8.1, Windows Store and Windows Desktop.
MIT License
224 stars 72 forks source link

Ntp server #78

Open acaliaro opened 8 years ago

acaliaro commented 8 years ago

Hi @rdavisau , how are you. Some times ago you have helped me writing this code

    public static class NtpClient
    {
        public static async Task<DateTime> GetNetworkTimeAsync()
        {

            Debug.WriteLine ("Entro in GetNTP " + DateTime.Now);
            //default Windows time server
            const string ntpServer = "time.windows.com";
            //const string ntpServer = "pool.ntp.org";
            //const string ntpServer = "time.nist.gov";

            // 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 tcs = new TaskCompletionSource<byte[]>();

            using (var socket = new UdpSocketReceiver())
            {
                socket.MessageReceived += async (sender, args) =>
                {
                    await socket.StopListeningAsync();
                    tcs.SetResult(args.ByteData);
                };

                Debug.WriteLine ("StartListening " + DateTime.Now);

                await socket.StartListeningAsync(); // any free port >1000 will do 

                Debug.WriteLine ("SendTo " + DateTime.Now);
                await socket.SendToAsync (ntpData, ntpServer, 123).ContinueWith(_=> Task.FromResult(true)).TimeoutAfter(TimeSpan.FromSeconds(3)); //.TimeoutAfter(TimeSpan.FromSeconds(3));
                Debug.WriteLine ("SendTo conclusa");

                ntpData = await tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(8));
            }

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

            Debug.WriteLine ("Esco da GetNTP " + DateTime.Now + " - " + networkDateTime + " - LOCALTIME: " + networkDateTime.ToLocalTime() + " - UTC: " + networkDateTime.ToUniversalTime());
            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));
        }
    }

Now I have a problem. I am using this code in XF PCL project. I have a wifi tablet that is connected to my home wifi. This code finish always with Timeout. If I activate my iPhone as hotspot and connect Android Tablet to iPhone hotspot, the same code start to works... There is a reason for this?

Thanks and sorry for my English. I hope you have understand...

acaliaro commented 8 years ago

@rdavisau have you some news about this problem? I am using this code in an app but It often returns a timeout

rdavisau commented 8 years ago

Hi Alessandro, sorry for the delay in getting back to you. It's hard to guess what could be the issue! Let's see:

Let me know what happens in those cases and maybe it will hint at something.. :shipit:

acaliaro commented 8 years ago

@rdavisau thanks for your answer. 1- there are not so much differences between wifi on or wifi off. 2- using ip address it seems to work well...very well.. I create an IPA and send it to my client so he will start some tests. Thanks!

acaliaro commented 8 years ago

by the way, after some other tests: 1: 3G works well than WiFi 2: ip address works well than "name coded" address

So, by now, 3G + Ip Address is the best solution

acaliaro commented 8 years ago

Hi @rdavisau I have left my iphone active this night and I have found it in "timeout" calling NTP. It has Wifi on. Now I have closed wifi and I try to leave my iphone without wifi so I can verify it if works well (by now... the answer if yes). I think there is something when it is connected with wifi...

rdavisau commented 8 years ago

Interesting - I suggested the IP address approach because I thought that the need to perform a DNS lookup on time.windows.com might be a factor. It has a TTL of 60 minutes though, so the lookup would only need to be performed once an hour.

But does the android wifi time out every time? It never succeeds?

acaliaro commented 8 years ago

I have only tried wit iOS. I try to develope an app that create a thread that every 20 seconds calls ntp and add the result (timeout or good) to a list or a counter so we can do some simple tests

acaliaro commented 8 years ago

Hi @rdavisau I am trying to create this "test app" but I have this problem...

https://www.dropbox.com/s/yt1m7oxkosfz66l/Screenshot%202016-04-21%2018.40.30.png?dl=0

acaliaro commented 8 years ago

ok Ryan, understand the problem...

acaliaro commented 8 years ago

So Ryan, I have done this little project

TestNtp

Every 10 seconds, Ntp server is called and a listview is filled with "good" or "bad" answer.

In this moment (iOS Iphone with Wifi ON) I have 60 OK and 15 BAD (25% bad). I continue the test. Thanks again for you help

rdavisau commented 8 years ago

Ok, I had a quick look at your repro - I don't think it will make any difference in this case but I would change your OnStart and OnResume like this:

protected override async void OnStart ()
{
    // Handle when your app starts
    await timer ();
}

(mark method async and await the call to timer()) In your case you are catching exceptions so I shouldn't matter but it may save you some problems later on.

Onto your results - UDP protocol is not guaranteed to deliver but to me 25% seems like a very high failure rate - especially if you are only sending requests every 10 seconds. Does the error rate increase or stay the same if you send every 5 seconds?

acaliaro commented 8 years ago

Hi Ryan. One night of test with a call every 10 seconds with Wifi ON and ip Address.. Result:

ok = 1884 bad = 1118

bad > 50%

Now I start test with wifi off

acaliaro commented 8 years ago

Without wifi

Ok 77 Bad 102

I think there is a problem.

@rdavisau have you some suggestions? Tia Alessandro

rdavisau commented 8 years ago

I think you should try to do a 'control' experiment against a service that is not time.windows.com. I wonder if there is a rate limit that you are hitting? Is it possible that after some number of requests from a single IP the NTP stops responding for a while?

acaliaro commented 8 years ago

So polling on various IP?

rdavisau commented 8 years ago

Maybe - I just wonder if time.windows.com is simply not responding sometimes. 50% is a very high timeout rate (I think).