Forum Xamarin.Forms

Announcement:

The Xamarin Forums have officially moved to the new Microsoft Q&A experience. Microsoft Q&A is the home for technical questions and answers at across all products at Microsoft now including Xamarin!

To create new threads and ask questions head over to Microsoft Q&A for .NET and get involved today.

socket plugin to access ntp server

AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

@RyanDavis I'have take a look to your SocketForPcl plugin (I hope you are the author :)).
I need to connect, via socket, to a ntp server.
I've found this code that should works

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

Is it possible to "translate " this code using your plugin?
Can you help me ?
Thanks
Alessandro

Posts

  • RyanDavisRyanDavis AUInsider, University ✭✭
    edited April 2015

    Hi Alessandro,

    Yes, I am the author! You can use sockets-for-pcl for this with a few modifications to the original code. I'm assuming you are coding in a PCL, and you have installed sockets-for-pcl in your PCL class and your platform projects.

    Firstly, the method signature should be made async-friendly:

    public static async Task<DateTime> GetNetworkTimeAsync()
    

    Then you should replace

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

    with

    var tcs = new TaskCompletionSource<byte[]>();
    using(var socket = new UdpSocketReceiver())
    {
       socket.MessageReceived += async (sender, args) =>
       {
            await socket.StopListeningAsync();
            tcs.SetResult(args.ByteData);
       };
    
       await socket.StartListeningAsync(11000); // any free port >1000 will do 
       await socket.SendToAsync(ntpData, ntpServer, 123);
    
       ntpData = await tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(3));
    }
    

    where TimeoutAfter is just an extension method on Task:

     public static class AsyncExtensions
        {
            public static async Task<T> TimeoutAfter<T>(this Task<T> task, TimeSpan timeout, CancellationTokenSource cancellationTokenSource = null)
            {
                if (task == await Task.WhenAny(task, Task.Delay(timeout)))
                    return await task;
                else
                {
                    if (cancellationTokenSource != null)
                        cancellationTokenSource.Cancel();
    
                    throw new TimeoutException();
                }
            }
        }
    

    Of course - TimeoutAfter throws TimeoutException, so you should be ready for that in your calling code.

    I tested this with Xamarin Forms on Android and it worked fine.

    Hope that helps!

  • MitchMilamMitchMilam USMember ✭✭✭

    @AlessandroCaliaro: Thanks for getting this figured out; and @RyanDavis thanks for the great work on the sockets PCL.

    I created a small repro here (mostly so I would not loose the code and idea).

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @MitchMilam thanks to you.

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @RyanDavis Thanks a lot for your help

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @RyanDavis, I'm trying to use @MitchMilam example, but I've 2 exception.

    One exception when wifi is down

        Unhandled Exception:
        [mono] System.ObjectDisposedException: The object was used after being disposed.
        [mono]   at System.Net.Sockets.UdpClient.CheckDisposed () [0x00000] in <filename unknown>:0 
        [mono]   at System.Net.Sockets.UdpClient.EndReceive (IAsyncResult asyncResult, System.Net.IPEndPoint& remoteEP) [0x00000] in <filename unknown>:0 
        [mono]   at System.Net.Sockets.UdpClient.<ReceiveAsync>m__0 (IAsyncResult r) [0x00000] in <filename unknown>:0 
        [mono]   at (wrapper delegate-invoke) System.Func`2<System.IAsyncResult, System.Net.Sockets.UdpReceiveResult>:invoke_TResult_T (System.IAsyncResult)
        [mono]   at System.Threading.Tasks.TaskFactory`1[System.Net.Sockets.UdpReceiveResult].InnerInvoke (System.Threading.Tasks.TaskCompletionSource`1 tcs, System.Func`2 endMethod, IAsyncResult l) [0x00000] in <filename unknown>:0 
        [mono] --- End of stack trace from previous location where exception was thrown ---
        [mono]   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0 
        [mono]   at System.Runtime.CompilerServices.TaskAwaiter`1[System.Net.Sockets.UdpReceiveResult].GetResult () [0x00000] in <filename unknown>:0 
        [mono]   at Sockets.Plugin.UdpSocketBase+<RunMessageReceiver>d__0.MoveNext () [0x00030] in c:\Users\rdavis\Dropbox\code\Sockets\Sockets\Sockets.Implementation.NET\UdpSocketBase.cs:37 
    

    One exception when wifi is up and, after some good read from ntp server, i receive this:

        Unhandled Exception:
        [mono] System.ObjectDisposedException: The object was used after being disposed.
        [mono]   at System.Net.Sockets.UdpClient.CheckDisposed () [0x00000] in <filename unknown>:0 
        [mono]   at System.Net.Sockets.UdpClient.EndReceive (IAsyncResult asyncResult, System.Net.IPEndPoint& remoteEP) [0x00000] in <filename unknown>:0 
        [mono]   at System.Net.Sockets.UdpClient.<ReceiveAsync>m__0 (IAsyncResult r) [0x00000] in <filename unknown>:0 
        [mono]   at (wrapper delegate-invoke) System.Func`2<System.IAsyncResult, System.Net.Sockets.UdpReceiveResult>:invoke_TResult_T (System.IAsyncResult)
        [mono]   at System.Threading.Tasks.TaskFactory`1[System.Net.Sockets.UdpReceiveResult].InnerInvoke (System.Threading.Tasks.TaskCompletionSource`1 tcs, System.Func`2 endMethod, IAsyncResult l) [0x00000] in <filename unknown>:0 
        [mono] --- End of stack trace from previous location where exception was thrown ---
        [mono]   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0 
        [mono]   at System.Runtime.CompilerServices.TaskAwaiter`1[System.Net.Sockets.UdpReceiveResult].GetResult () [0x00000] in <filename unknown>:0 
        [mono]   at Sockets.Plugin.UdpSocketBase+<RunMessageReceiver>d__0.MoveNext () [0x00030] in c:\Users\rdavis\Dropbox\code\Sockets\Sockets\Sockets.Implementation.NET\UdpSocketBase.cs:37 
        [OpenGLRenderer] prepareDirty (0.00, 0.00, 600.00, 976.00) opaque 1 <0x664bea88>
        [OpenGLRenderer] finish <0x664bea88>
        [libc] bionic/libstdc++/src/pure_virtual.cpp:6: void __cxa_pure_virtual(): assertion "!"Pure virtual function called. Are you calling virtual methods from a destructor?"" failed
        [libc] Fatal signal 6 (SIGABRT) at 0x00000d0d (code=-6), thread 3341 (ePresenze.Droid)
        [libc] Send stop signal to pid:3341 in void debuggerd_signal_handler(int, siginfo_t*, void*)
    

    Can you help me?
    Thanks

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @RyanDavis, I'm trying to use @MitchMilam example, but I've 2 exception.

    One exception when wifi is down

        Unhandled Exception:
        [mono] System.ObjectDisposedException: The object was used after being disposed.
        [mono]   at System.Net.Sockets.UdpClient.CheckDisposed () [0x00000] in <filename unknown>:0 
        [mono]   at System.Net.Sockets.UdpClient.EndReceive (IAsyncResult asyncResult, System.Net.IPEndPoint& remoteEP) [0x00000] in <filename unknown>:0 
        [mono]   at System.Net.Sockets.UdpClient.<ReceiveAsync>m__0 (IAsyncResult r) [0x00000] in <filename unknown>:0 
        [mono]   at (wrapper delegate-invoke) System.Func`2<System.IAsyncResult, System.Net.Sockets.UdpReceiveResult>:invoke_TResult_T (System.IAsyncResult)
        [mono]   at System.Threading.Tasks.TaskFactory`1[System.Net.Sockets.UdpReceiveResult].InnerInvoke (System.Threading.Tasks.TaskCompletionSource`1 tcs, System.Func`2 endMethod, IAsyncResult l) [0x00000] in <filename unknown>:0 
        [mono] --- End of stack trace from previous location where exception was thrown ---
        [mono]   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0 
        [mono]   at System.Runtime.CompilerServices.TaskAwaiter`1[System.Net.Sockets.UdpReceiveResult].GetResult () [0x00000] in <filename unknown>:0 
        [mono]   at Sockets.Plugin.UdpSocketBase+<RunMessageReceiver>d__0.MoveNext () [0x00030] in c:\Users\rdavis\Dropbox\code\Sockets\Sockets\Sockets.Implementation.NET\UdpSocketBase.cs:37 
    

    One exception when wifi is up and, after some good read from ntp server, i receive this:

        Unhandled Exception:
        [mono] System.ObjectDisposedException: The object was used after being disposed.
        [mono]   at System.Net.Sockets.UdpClient.CheckDisposed () [0x00000] in <filename unknown>:0 
        [mono]   at System.Net.Sockets.UdpClient.EndReceive (IAsyncResult asyncResult, System.Net.IPEndPoint& remoteEP) [0x00000] in <filename unknown>:0 
        [mono]   at System.Net.Sockets.UdpClient.<ReceiveAsync>m__0 (IAsyncResult r) [0x00000] in <filename unknown>:0 
        [mono]   at (wrapper delegate-invoke) System.Func`2<System.IAsyncResult, System.Net.Sockets.UdpReceiveResult>:invoke_TResult_T (System.IAsyncResult)
        [mono]   at System.Threading.Tasks.TaskFactory`1[System.Net.Sockets.UdpReceiveResult].InnerInvoke (System.Threading.Tasks.TaskCompletionSource`1 tcs, System.Func`2 endMethod, IAsyncResult l) [0x00000] in <filename unknown>:0 
        [mono] --- End of stack trace from previous location where exception was thrown ---
        [mono]   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x00000] in <filename unknown>:0 
        [mono]   at System.Runtime.CompilerServices.TaskAwaiter`1[System.Net.Sockets.UdpReceiveResult].GetResult () [0x00000] in <filename unknown>:0 
        [mono]   at Sockets.Plugin.UdpSocketBase+<RunMessageReceiver>d__0.MoveNext () [0x00030] in c:\Users\rdavis\Dropbox\code\Sockets\Sockets\Sockets.Implementation.NET\UdpSocketBase.cs:37 
        [OpenGLRenderer] prepareDirty (0.00, 0.00, 600.00, 976.00) opaque 1 <0x664bea88>
        [OpenGLRenderer] finish <0x664bea88>
        [libc] bionic/libstdc++/src/pure_virtual.cpp:6: void __cxa_pure_virtual(): assertion "!"Pure virtual function called. Are you calling virtual methods from a destructor?"" failed
        [libc] Fatal signal 6 (SIGABRT) at 0x00000d0d (code=-6), thread 3341 (ePresenze.Droid)
        [libc] Send stop signal to pid:3341 in void debuggerd_signal_handler(int, siginfo_t*, void*)
    

    Can you help me?
    Thanks

  • RyanDavisRyanDavis AUInsider, University ✭✭

    Hi @AlessandroCaliaro,

    Thanks for posting these. I've been away from my machine the last couple of days but should be able to take a look tonight/tomorrow!

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    no problem @RyanDavis . I can say that the method to retrive NTP is inside a timer (it is called every X seconds)

  • RyanDavisRyanDavis AUInsider, University ✭✭
    edited April 2015

    @AlessandroCaliaro I have pushed version 1.1.7 of sockets-for-pcl to NuGet; I think should address the problems you found.

    I have also added support to UdpSocketReceiver for delegating the choice of listening port to the operating system. Querying NTP time is definitely a situation in which you should take advantage of this. To do so, change

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

    to

    await socket.StartListeningAsync(); 
    

    @MitchMilam, can you update your sample with the above change?

  • MitchMilamMitchMilam USMember ✭✭✭
  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @ryandavis, how are you? I'm here again for a "problem" accessing NTP.
    I'm using @MitchMilam example you have already worked for.
    The problem is: when , for some reason, I have no data from NTP server, the socket never goes in timeout, and it waits, and waits...
    In the log I have
    StartListening
    SendTo

    and nothing else...

    Can you help me?
    Thanks
    Alessandro

            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);
    
                ntpData = await tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(3));
            }
    
            //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);
            return networkDateTime.ToLocalTime();
    
  • RyanDavisRyanDavis AUInsider, University ✭✭

    Hi @AlessandroCaliaro, all is well thanks!

    First I would add a log statement after await socket.SendToAsync(ntpData, ntpServer, 123); to confirm that you are making it past that call. Then, it would be useful to just check the method signature and the location where you call this code. Can you include the full method that you have sampled above, and the full method of the caller (or at least the signature and the line where you call this code)?

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    Thanks @RyanDavis for your answer.
    Correct, a "log" after SendToAsync is good.
    Anyway, the "log" before "return networkDateTime.ToLocalTime()" is not reached.
    If I remember correctly, I have had the problem only on Samsung galaxy s5

    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";
    
            // 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);
    
                ntpData = await tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(3));
            }
    
            //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);
            return networkDateTime.ToLocalTime();
        }
    

    I use this static method here

        private static async Task ntp(){
            App.UltimaDataOraNtp = await NtpClient.GetNetworkTimeAsync();
        }
    

    And I call ntp() here

        public static async Task UpdateDateTime(bool allinvio = false){
    
            Debug.WriteLine ("ENTRO IN UPDATETIME " + DateTime.Now);
            App.UltimaDataOraDispositivo = DateTime.Now;
            App.UltimaDataOraNtp = DateTime.MinValue;
    
    
            try {
    
                //bool isNtpOk = false;
                bool trovataDataOra = false ;
                try{
                    Debug.WriteLine("Verifico connettività");
                    if(Connectivity.Plugin.CrossConnectivity.Current.IsConnected){
                        Debug.WriteLine("Connettività presente");
                        await **ntp();**
                    //  isNtpOk = true;
                        if(App.UltimaDataOraNtp != DateTime.MinValue){
                            App.Globali.UltimaDataOraElaborata = App.UltimaDataOraNtp;
                            App.TipoDataOra = App.EnumTipoDataOra.Ntp;
                            App.Globali.Provenienza = "NTP";
                            trovataDataOra = true;
                        }
    
                    }
                    else {
                        Debug.WriteLine("Connettività assente");
                        throw new Exception("NORETE");
                    }
                }
                catch{
                }
    
                //bool isGpsOk = false;
                bool readGps = false; // Quando sono all'invio dei dati, leggo il gps solo se i dati sono più vecchi dell'ultimo minuto
                if(allinvio){
                    if(App.UltimaDataOraDispositivo.Subtract(App.UltimaRilevazioneGps).TotalMinutes > 1)
                        readGps = true;
                }
    
                if(!allinvio || 
                    !trovataDataOra || 
                    readGps){
                    try{
                        await gps();
                //      isGpsOk = true;
                        if(!trovataDataOra && App.Globali.Gps.DateTime != DateTimeOffset.MinValue){
                            App.Globali.UltimaDataOraElaborata = App.Globali.Gps.DateTime.DateTime.ToLocalTime();
                            if(DateTime.Now.IsDaylightSavingTime() && DeviceInfo.Plugin.CrossDeviceInfo.Current.Platform == DeviceInfo.Plugin.Abstractions.Platform.iOS)
                                App.Globali.UltimaDataOraElaborata = App.Globali.UltimaDataOraElaborata.Subtract(new TimeSpan(1,0,0));
                            App.TipoDataOra = App.EnumTipoDataOra.Gps;
                            App.Globali.Provenienza = "GPS";
                            trovataDataOra = true;
                        }
                        if(App.Globali.Gps.DateTime != DateTimeOffset.MinValue)
                            App.UltimaRilevazioneGps = DateTime.Now; // Memorizzo ultima dataora di rilevazione gps
    
                    }
                    catch{
                    }
                }
    
                if(!trovataDataOra){
                    App.Globali.UltimaDataOraElaborata = App.UltimaDataOraDispositivo;
                    App.TipoDataOra = App.EnumTipoDataOra.Dispositivo;
                    App.Globali.Provenienza = "DISPOSITIVO";
                    trovataDataOra = true;
                    }
    
            }
            catch(Exception ex){
                Debug.WriteLine(ex.Message);
            }
            finally{
            }
    
        }
    
  • RyanDavisRyanDavis AUInsider, University ✭✭

    Hi @AlessandroCaliaro

    If I'm not mistaken, your await ntp() call is wrapped in a try block with an empty catch clause. If your ntp() call times out, it will throw a TimeoutException but it will be swallowed. If you check for the exception there, it might be that it did timeout after all. As for why it times out.. that can be the next problem :)

    If not -- you mentioned the Samsung Galaxy - does that mean you don't have the problem in the emulator? I have seen a few instances of socket/networking functions not working well on some specific android devices at certain versions, but I haven't been able to pinpoint the exact details. What version of Android your Galaxy running on?

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    No, in the emulator it never stop. I say "it never stop" because I call "UpdateDateTime" (where there is the empty catch) every minute with this Task

        async void TimerRunning(int delay, CancellationToken token)
        {
            //var startTime = DateTime.Now;
            //var watch = Stopwatch.StartNew();
            Debug.WriteLine ("Timer attivato");
            while (!token.IsCancellationRequested)
            {
                try
                {
                    //await Task.Delay(60000 - (int)(watch.ElapsedMilliseconds%1000), token);
                    await Task.Delay(delay , token);
                    delay = 60000;
                    Debug.WriteLine (DateTime.Now.ToLocalTime ().ToString () + " DELAY: " + delay);
    
                }
                catch (TaskCanceledException)
                {
                }
    
                //Device.BeginInvokeOnMainThread(() => this.lblTimerText.Text = (DateTime.Now - startTime).ToString());
                //Device.BeginInvokeOnMainThread(() => this._labelOra.Text = watch.Elapsed.ToString());
                if (!token.IsCancellationRequested) {
                    await **App.UpdateDateTime** ();
                    Debug.WriteLine ("TimerRunning ");// + watch.Elapsed.ToString ());
                }
            }
        }
    

    Also if the socket goes in timeout, The timer should restart the next minute and call again UpdateDateTime().
    But it does not restart. As it was "blocked" somewhere.

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @RyanDavis, I've inserted a Log after SendToAsync and a Log in "empty catch".

                Debug.WriteLine ("SendTo " + DateTime.Now);
                await socket.SendToAsync(ntpData, ntpServer, 123);
                Debug.WriteLine ("SendTo conclusa");
    

    Normally I have

    SendTo 21/06/2015 13:46:03
    SendTo 21/06/2015 13:46:03
    SendTo conclusa (ended)
    SendTo conclusa

    If I disable wifi on my mac (I', working with emulator...), I have the exception

    SendTo 21/06/2015 13:47:04
    SendTo 21/06/2015 13:47:04
    Errore in NTP(): Could not resolve host 'time.windows.com'
    Errore in NTP(): Could not resolve host 'time.windows.com'

    And the timer goes on.
    If I activate the wifi on my mac, I have again a correct log

    SendTo 21/06/2015 13:51:04
    SendTo 21/06/2015 13:51:04
    SendTo conclusa
    SendTo conclusa

  • RyanDavisRyanDavis AUInsider, University ✭✭
    edited June 2015

    @AlessandroCaliaro

    Ah ok, yeah I see what you mean.

    I noticed that the timer method is an async void, which should usually be avoided and if an exception were to somehow bubble up to that method it could break the timer. It doesn't look like there are any points where that can happen, but just to be sure you should change it to async Task and check any exceptions. Since you want to loop "forever" (until cancelled), you can't await the method but you can set it and see the exceptions that come back.

    I have used an approach here (see L64 for the 'forever' method, and L34 for the invocation). Change your method signature to async Task TimerRunning. and where you call it, wrap it in Task.Run() and add a ContinueWith to handle any exception that occurs. Something like this:

    Task.Run(()=> TimerRunning())
       .ContinueWith(t=> {
            var ex = t.Exception.InnerException;
            Debug.WriteLine("com'e' morto il timer? - " + ex.Message); // if there is an exception, we will see it here
        }, TaskContinuationOptions.OnlyOnFaulted);
    

    For this you will have to test on the physical device again!

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    Thanks Ryan. I take a look.

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @RyanDavis Hi my friend.
    I am here again... to disturb you.

    The problem is always on NTP server.
    The code I use is the same but with more "logs"

        public static async Task<DateTime> GetNetworkTimeAsync()
        {
    
            Debug.WriteLine ("Entro in GetNTP " + DateTime.Now);
            //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 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);
                Debug.WriteLine ("SendTo conclusa");
    
                ntpData = await tcs.Task.TimeoutAfter(TimeSpan.FromSeconds(3));
            }
    
            //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();
        }
    

    I think to have found a situation where log are
    Debug.WriteLine ("StartListening " + DateTime.Now);
    Debug.WriteLine ("SendTo " + DateTime.Now);

    and stop!

                Debug.WriteLine ("SendTo conclusa");
    

    does not appear.

    If you would like to try, I have a android or iOS phone connected via wifi to a router.
    All works fine, (timer continue to works every minute and NTP return the time), but if I remove the internet cable from the router, the problem appears. It seems that phone is connected to router but is not connected to internet. This situation block the communication with NTP and I don't receive any timeout exception.

    I think you can test the GetNetworkTimeAsync also without use a "timer". I've it in a Task.Run(async....) in OnStart method.

    Thanks for your help
    Alessandro.

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    @RyanDavis , sorry if I disturb you again. Have you an answer for my problem?
    Thanks
    Alessandro

  • RyanDavisRyanDavis AUInsider, University ✭✭

    Hi @AlessandroCaliaro,

    I haven't had time to look closely, but if you are not connected to the internet you may receive a different exception immediately at the SendTo call; or it may hang. Can you add .TimeoutAfter() to the SendToAsync call as well, and see whether it times out?

    await socket.SendToAsync(ntpData, ntpServer, 123)
                .TimeoutAfter(TimeSpan.FromSeconds(3));
    
  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    Thansk Ryan. Is Sunday, I know you are there :)

    If I modify

        await socket.SendToAsync(ntpData, ntpServer, 123)
    

    to

        await socket.SendToAsync(ntpData, ntpServer, 123).TimeoutAfter(TimeSpan.FromSeconds(3));
    

    I have a compile error

    /Users/alessandrocaliaro/Projects/Soluzione1/IWapp/IWapp/NTPClient.cs(55,55): Error CS0411: The type arguments for method `IWapp.AsyncExtensions.TimeoutAfter(this System.Threading.Tasks.Task, System.TimeSpan, System.Threading.CancellationTokenSource)' cannot be inferred from the usage. Try specifying the type arguments explicitly (CS0411) (IWapp)

    If i press "." after ,123) intellisense does not give me "TimeoutAfter" method.

    Alessandro

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    I don't know what there is...
    Now I have always a "timeout" every time I try to access to NTP server.
    I have also downloaded from github @MitchMilam demo project. It also goes in timeout.
    Could be down the Microsoft NTP server?

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    ok, the last post was a wifi problem. Sorry.
    Exist alway the problem here
    https://forums.xamarin.com/discussion/comment/135615/#Comment_135615

    Thanks
    Alessandro

  • RyanDavisRyanDavis AUInsider, University ✭✭
    edited July 2015

    Hi @AlessandroCaliaro,

    Sorry I didn't realise you had replied the other day.

    I didn't think of it before - but TimeoutAfter expects Task<T> whereas SendToAsync is just Task. You will need another overload for TimeoutAfter that expects just Task. From memory this was a little more painful to create than expected so in the meantime, just for debugging purposes, can you change it to this:

    await socket.SendToAsync(ntpData, ntpServer, 123)
                .ContinueWith(_=> Task.FromResult(true)) // so we return Task<bool>
                .TimeoutAfter(TimeSpan.FromSeconds(3));
    

    which I expect will compile. Then we can see again whether it times out.

  • AlessandroCaliaroAlessandroCaliaro ITMember ✭✭✭✭✭

    No problem Ryan and thanks always for your help.
    I have this news.
    With your suggestion, now the thread does not block. It's very good.
    I have only a delay between the SendTo and SendTo Conclusa.
    It's 30 seconds...
    Where it takes this timeout? I aspect 3 seconds...

    SendTo 7/2/2015 5:17:58 PM
    2015-07-02 17:18:28.417 IWapp.iOS[6700:284949] SendTo conclusa
    SendTo conclusa
    2015-07-02 17:18:31.418 IWapp.iOS[6700:284949] Errore in NTP(): The operation has timed-out.
    Errore in NTP(): The operation has timed-out.

Sign In or Register to comment.