Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SntpClient failure notification #12

Open
trailway opened this issue Aug 26, 2015 · 8 comments
Open

SntpClient failure notification #12

trailway opened this issue Aug 26, 2015 · 8 comments

Comments

@trailway
Copy link
Contributor

The SntpClient class is notifying failures multiple times. This can be reproduced by pulling the network cable from machine. I added the following check to avoid this:

    public void BeginGetDate(Action<DateTime> getTime, Action failure)
    {
        _index += 1;
        if (_hosts.Length <= _index)
        {
            if (_hosts.Length == _index)
            {
                failure();
            }
            return;
        }

I suspect that there may be a deeper problem here but the fix isn't obvious to me.

@ayende
Copy link
Owner

ayende commented Aug 27, 2015

The actual reason for the failure isn't that interesting. It can be a network cable not connected, bad WIFI connection,etc.

We want to handle all errors in the same manner

@trailway
Copy link
Contributor Author

I think that the problem is that MaybeOperationTimeout is not checking to see if a socket was created:

    private void MaybeOperationTimeout(object state, bool timedOut)
    {
        if (timedOut == false)
            return;

        var theState = (State)state;
        try
        {
            theState.Socket?.Close();
        }

This results in an unnecessary retry because Dns.BeginGetHostAddresses is failing (it takes longer than 1/2 second) and there is no socket.

@trailway
Copy link
Contributor Author

The problem is that it is notifying the failure multiple times. I think
that it should either call the success or failure function once.

I think that the root cause of the problem that MaybeOperationTimeout is
called from RegisterWaitForTimeout which is called from BeginGetDate and
there is a timeout but no socket. Dns.BeginGetHostAddresses throws an
exception (it takes a while) which causes a recursive BeginGetDate. The
null socket also causes a BeginGetDate as well.

What I did was:

  1. Only notify 1 time
  2. Test if the socket is null in MaybeOperationTimeout

Thanks for contributing the code.

On Thu, Aug 27, 2015 at 12:18 AM, Ayende Rahien [email protected]
wrote:

The actual reason for the failure isn't that interesting. It can be a
network cable not connected, bad WIFI connection,etc.

We want to handle all errors in the same manner


Reply to this email directly or view it on GitHub
#12 (comment)
.

@ayende
Copy link
Owner

ayende commented Aug 28, 2015

Can you show your changes? I'm not sure that I can follow from the description.

@trailway
Copy link
Contributor Author

See attached code.

On Fri, Aug 28, 2015 at 5:53 AM, Ayende Rahien [email protected]
wrote:

Can you show your changes? I'm not sure that I can follow from the
description.


Reply to this email directly or view it on GitHub
#12 (comment)
.

using System;
using System.CodeDom.Compiler;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace LicensingShared
{
///

/// Provides support for asynchronously acquiring the current time
/// via well known sources for time.
/// </summary>


/// <remarks>
/// This code is taken from <a href="https://github.com/ayende/rhino-licensing">rhino-licensing</a>.
/// See <a href="https://github.com/ayende/rhino-licensing/blob/master/license.txt">copyright notice</a>.
/// Minor corrections where made to the code as a result of testing.
/// </remarks>
[GeneratedCode(@"rhino-licensing", @"08/25/2015")]
internal class SntpClient
{
    private const byte SntpDataLength = 48;
    private readonly string[] _hosts;
    private int _index = -1;
    public SntpClient(string[] hosts)
    {
        _hosts = hosts;
    }

    private static bool GetIsServerMode(byte[] sntpData)
    {
        return (sntpData[0] & 0x7) == 4 /* server mode */;
    }

    private static DateTime GetTransmitTimestamp(byte[] sntpData)
    {
        var milliSeconds = GetMilliSeconds(sntpData, 40);
        return ComputeDate(milliSeconds);
    }

    private static DateTime ComputeDate(ulong milliseconds)
    {
        return new DateTime(1900, 1, 1).Add(TimeSpan.FromMilliseconds(milliseconds));
    }

    private static ulong GetMilliSeconds(byte[] sntpData, byte offset)
    {
        ulong intpart = 0, fractpart = 0;

        for (var i = 0; i <= 3; i++)
        {
            intpart = 256 * intpart + sntpData[offset + i];
        }
        for (var i = 4; i <= 7; i++)
        {
            fractpart = 256 * fractpart + sntpData[offset + i];
        }
        var milliseconds = intpart * 1000 + (fractpart * 1000) / 0x100000000L;
        return milliseconds;
    }

    public void BeginGetDate(Action<DateTime> getTime, Action failure)
    {
        _index += 1;
        if (_hosts.Length <= _index)
        {
            if (_hosts.Length == _index)
            {
                failure();
            }
            return;
        }
        try
        {
            var host = _hosts[_index];
            var state = new State(null, null, getTime, failure);
            var result = Dns.BeginGetHostAddresses(host, EndGetHostAddress, state);
            RegisterWaitForTimeout(state, result);
        }
        catch (Exception)
        {
            // retry, recursion stops at the end of the hosts
            BeginGetDate(getTime, failure);
        }
    }

    private void EndGetHostAddress(IAsyncResult asyncResult)
    {
        var state = (State)asyncResult.AsyncState;
        try
        {
            var addresses = Dns.EndGetHostAddresses(asyncResult);
            var endPoint = new IPEndPoint(addresses[0], 123);

            var socket = new UdpClient();
            socket.Connect(endPoint);
            socket.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 500);
            socket.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 500);
            var sntpData = new byte[SntpDataLength];
            sntpData[0] = 0x1B; // version = 4 & mode = 3 (client)

            var newState = new State(socket, endPoint, state.GetTime, state.Failure);
            var result = socket.BeginSend(sntpData, sntpData.Length, EndSend, newState);
            RegisterWaitForTimeout(newState, result);
        }
        catch (Exception)
        {
            // retry, recursion stops at the end of the hosts
            BeginGetDate(state.GetTime, state.Failure);
        }
    }

    private void RegisterWaitForTimeout(State newState, IAsyncResult result)
    {
        if (result != null)
        {
            ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, MaybeOperationTimeout, newState, 500,
                true);
        }
    }

    private void MaybeOperationTimeout(object state, bool timedOut)
    {
        if (timedOut == false)
            return;

        var theState = (State)state;
        try
        {
            theState.Socket?.Close();
        }
        catch (Exception)
        {
            // retry, recursion stops at the end of the hosts
            BeginGetDate(theState.GetTime, theState.Failure);
        }
    }

    private void EndSend(IAsyncResult ar)
    {
        var state = (State)ar.AsyncState;
        try
        {
            state.Socket.EndSend(ar);
            var result = state.Socket.BeginReceive(EndReceive, state);
            RegisterWaitForTimeout(state, result);
        }
        catch
        {
            state.Socket.Close();
            BeginGetDate(state.GetTime, state.Failure);
        }
    }

    private void EndReceive(IAsyncResult ar)
    {
        var state = (State)ar.AsyncState;
        try
        {
            var endPoint = state.EndPoint;
            var sntpData = state.Socket.EndReceive(ar, ref endPoint);
            if (IsResponseValid(sntpData) == false)
            {
                state.Failure();
                return;
            }
            var transmitTimestamp = GetTransmitTimestamp(sntpData);
            state.GetTime(transmitTimestamp);
        }
        catch
        {
            BeginGetDate(state.GetTime, state.Failure);
        }
        finally
        {
            state.Socket.Close();
        }
    }

    private bool IsResponseValid(byte[] sntpData)
    {
        return sntpData.Length >= SntpDataLength && GetIsServerMode(sntpData);
    }

    #region Nested type: State

    private class State
    {
        public State(UdpClient socket, IPEndPoint endPoint, Action<DateTime> getTime, Action failure)
        {
            Socket = socket;
            EndPoint = endPoint;
            GetTime = getTime;
            Failure = failure;
        }

        public UdpClient Socket { get; private set; }

        public Action<DateTime> GetTime { get; private set; }

        public Action Failure { get; private set; }

        public IPEndPoint EndPoint { get; private set; }
    }

    #endregion
}

}

@ayende
Copy link
Owner

ayende commented Aug 28, 2015

I can't really read this. Can you send this as a PR, that way it would be
much easier to see the changes.

*Hibernating Rhinos Ltd *

Oren Eini* l CEO l *Mobile: + 972-52-548-6969

Office: +972-4-622-7811 *l *Fax: +972-153-4-622-7811

On Fri, Aug 28, 2015 at 4:54 PM, trailway [email protected] wrote:

See attached code.

On Fri, Aug 28, 2015 at 5:53 AM, Ayende Rahien [email protected]
wrote:

Can you show your changes? I'm not sure that I can follow from the
description.


Reply to this email directly or view it on GitHub
<
#12 (comment)

.

using System;
using System.CodeDom.Compiler;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace LicensingShared
{
///

/// Provides support for asynchronously acquiring the current time
/// via well known sources for time.
///

///
/// This code is taken from rhino-licensing.
/// See copyright
notice.
/// Minor corrections where made to the code as a result of testing.
///
[GeneratedCode(@"rhino-licensing", @"08/25/2015")]
internal class SntpClient
{
private const byte SntpDataLength = 48;
private readonly string[] _hosts;
private int _index = -1;

public SntpClient(string[] hosts)
{
_hosts = hosts;
}

private static bool GetIsServerMode(byte[] sntpData)
{
return (sntpData[0] & 0x7) == 4 /* server mode */;
}

private static DateTime GetTransmitTimestamp(byte[] sntpData)
{
var milliSeconds = GetMilliSeconds(sntpData, 40);
return ComputeDate(milliSeconds);
}

private static DateTime ComputeDate(ulong milliseconds)
{
return new DateTime(1900, 1,
1).Add(TimeSpan.FromMilliseconds(milliseconds));
}

private static ulong GetMilliSeconds(byte[] sntpData, byte offset)
{
ulong intpart = 0, fractpart = 0;

for (var i = 0; i <= 3; i++)
{
intpart = 256 * intpart + sntpData[offset + i];
}
for (var i = 4; i <= 7; i++)
{
fractpart = 256 * fractpart + sntpData[offset + i];
}
var milliseconds = intpart * 1000 + (fractpart * 1000) / 0x100000000L;
return milliseconds;
}

public void BeginGetDate(Action getTime, Action failure)
{
_index += 1;
if (_hosts.Length <= _index)
{
if (_hosts.Length == _index)
{
failure();
}
return;
}
try
{
var host = _hosts[_index];
var state = new State(null, null, getTime, failure);
var result = Dns.BeginGetHostAddresses(host, EndGetHostAddress, state);
RegisterWaitForTimeout(state, result);
}
catch (Exception)
{
// retry, recursion stops at the end of the hosts
BeginGetDate(getTime, failure);
}
}

private void EndGetHostAddress(IAsyncResult asyncResult)
{
var state = (State)asyncResult.AsyncState;
try
{
var addresses = Dns.EndGetHostAddresses(asyncResult);
var endPoint = new IPEndPoint(addresses[0], 123);

var socket = new UdpClient();
socket.Connect(endPoint);
socket.Client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout, 500);
socket.Client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.SendTimeout, 500);
var sntpData = new byte[SntpDataLength];
sntpData[0] = 0x1B; // version = 4 & mode = 3 (client)

var newState = new State(socket, endPoint, state.GetTime, state.Failure);
var result = socket.BeginSend(sntpData, sntpData.Length, EndSend,
newState);
RegisterWaitForTimeout(newState, result);
}
catch (Exception)
{
// retry, recursion stops at the end of the hosts
BeginGetDate(state.GetTime, state.Failure);
}
}

private void RegisterWaitForTimeout(State newState, IAsyncResult result)
{
if (result != null)
{
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
MaybeOperationTimeout, newState, 500,
true);
}
}

private void MaybeOperationTimeout(object state, bool timedOut)
{
if (timedOut == false)
return;

var theState = (State)state;
try
{
theState.Socket?.Close();
}
catch (Exception)
{
// retry, recursion stops at the end of the hosts
BeginGetDate(theState.GetTime, theState.Failure);
}
}

private void EndSend(IAsyncResult ar)
{
var state = (State)ar.AsyncState;
try
{
state.Socket.EndSend(ar);
var result = state.Socket.BeginReceive(EndReceive, state);
RegisterWaitForTimeout(state, result);
}
catch
{
state.Socket.Close();
BeginGetDate(state.GetTime, state.Failure);
}
}

private void EndReceive(IAsyncResult ar)
{
var state = (State)ar.AsyncState;
try
{
var endPoint = state.EndPoint;
var sntpData = state.Socket.EndReceive(ar, ref endPoint);
if (IsResponseValid(sntpData) == false)
{
state.Failure();
return;
}
var transmitTimestamp = GetTransmitTimestamp(sntpData);
state.GetTime(transmitTimestamp);
}
catch
{
BeginGetDate(state.GetTime, state.Failure);
}
finally
{
state.Socket.Close();
}
}

private bool IsResponseValid(byte[] sntpData)
{
return sntpData.Length >= SntpDataLength && GetIsServerMode(sntpData);
}

#region Nested type: State

private class State
{
public State(UdpClient socket, IPEndPoint endPoint, Action
getTime, Action failure)
{
Socket = socket;
EndPoint = endPoint;
GetTime = getTime;
Failure = failure;
}

public UdpClient Socket { get; private set; }

public Action GetTime { get; private set; }

public Action Failure { get; private set; }

public IPEndPoint EndPoint { get; private set; }
}

#endregion
}
}


Reply to this email directly or view it on GitHub
#12 (comment)
.

@trailway
Copy link
Contributor Author

Done.

My first pull request.

On Fri, Aug 28, 2015 at 11:07 AM, Ayende Rahien [email protected]
wrote:

I can't really read this. Can you send this as a PR, that way it would be
much easier to see the changes.

*Hibernating Rhinos Ltd *

Oren Eini* l CEO l *Mobile: + 972-52-548-6969

Office: +972-4-622-7811 *l *Fax: +972-153-4-622-7811

On Fri, Aug 28, 2015 at 4:54 PM, trailway [email protected]
wrote:

See attached code.

On Fri, Aug 28, 2015 at 5:53 AM, Ayende Rahien <[email protected]

wrote:

Can you show your changes? I'm not sure that I can follow from the
description.


Reply to this email directly or view it on GitHub
<

#12 (comment)

.

using System;
using System.CodeDom.Compiler;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace LicensingShared
{
///

/// Provides support for asynchronously acquiring the current time
/// via well known sources for time.
///

///
/// This code is taken from rhino-licensing.
/// See copyright
notice.
/// Minor corrections where made to the code as a result of testing.
///
[GeneratedCode(@"rhino-licensing", @"08/25/2015")]
internal class SntpClient
{
private const byte SntpDataLength = 48;
private readonly string[] _hosts;
private int _index = -1;

public SntpClient(string[] hosts)
{
_hosts = hosts;
}

private static bool GetIsServerMode(byte[] sntpData)
{
return (sntpData[0] & 0x7) == 4 /* server mode */;
}

private static DateTime GetTransmitTimestamp(byte[] sntpData)
{
var milliSeconds = GetMilliSeconds(sntpData, 40);
return ComputeDate(milliSeconds);
}

private static DateTime ComputeDate(ulong milliseconds)
{
return new DateTime(1900, 1,
1).Add(TimeSpan.FromMilliseconds(milliseconds));
}

private static ulong GetMilliSeconds(byte[] sntpData, byte offset)
{
ulong intpart = 0, fractpart = 0;

for (var i = 0; i <= 3; i++)
{
intpart = 256 * intpart + sntpData[offset + i];
}
for (var i = 4; i <= 7; i++)
{
fractpart = 256 * fractpart + sntpData[offset + i];
}
var milliseconds = intpart * 1000 + (fractpart * 1000) / 0x100000000L;
return milliseconds;
}

public void BeginGetDate(Action getTime, Action failure)
{
_index += 1;
if (_hosts.Length <= _index)
{
if (_hosts.Length == _index)
{
failure();
}
return;
}
try
{
var host = _hosts[_index];
var state = new State(null, null, getTime, failure);
var result = Dns.BeginGetHostAddresses(host, EndGetHostAddress, state);
RegisterWaitForTimeout(state, result);
}
catch (Exception)
{
// retry, recursion stops at the end of the hosts
BeginGetDate(getTime, failure);
}
}

private void EndGetHostAddress(IAsyncResult asyncResult)
{
var state = (State)asyncResult.AsyncState;
try
{
var addresses = Dns.EndGetHostAddresses(asyncResult);
var endPoint = new IPEndPoint(addresses[0], 123);

var socket = new UdpClient();
socket.Connect(endPoint);
socket.Client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout, 500);
socket.Client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.SendTimeout, 500);
var sntpData = new byte[SntpDataLength];
sntpData[0] = 0x1B; // version = 4 & mode = 3 (client)

var newState = new State(socket, endPoint, state.GetTime, state.Failure);
var result = socket.BeginSend(sntpData, sntpData.Length, EndSend,
newState);
RegisterWaitForTimeout(newState, result);
}
catch (Exception)
{
// retry, recursion stops at the end of the hosts
BeginGetDate(state.GetTime, state.Failure);
}
}

private void RegisterWaitForTimeout(State newState, IAsyncResult result)
{
if (result != null)
{
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
MaybeOperationTimeout, newState, 500,
true);
}
}

private void MaybeOperationTimeout(object state, bool timedOut)
{
if (timedOut == false)
return;

var theState = (State)state;
try
{
theState.Socket?.Close();
}
catch (Exception)
{
// retry, recursion stops at the end of the hosts
BeginGetDate(theState.GetTime, theState.Failure);
}
}

private void EndSend(IAsyncResult ar)
{
var state = (State)ar.AsyncState;
try
{
state.Socket.EndSend(ar);
var result = state.Socket.BeginReceive(EndReceive, state);
RegisterWaitForTimeout(state, result);
}
catch
{
state.Socket.Close();
BeginGetDate(state.GetTime, state.Failure);
}
}

private void EndReceive(IAsyncResult ar)
{
var state = (State)ar.AsyncState;
try
{
var endPoint = state.EndPoint;
var sntpData = state.Socket.EndReceive(ar, ref endPoint);
if (IsResponseValid(sntpData) == false)
{
state.Failure();
return;
}
var transmitTimestamp = GetTransmitTimestamp(sntpData);
state.GetTime(transmitTimestamp);
}
catch
{
BeginGetDate(state.GetTime, state.Failure);
}
finally
{
state.Socket.Close();
}
}

private bool IsResponseValid(byte[] sntpData)
{
return sntpData.Length >= SntpDataLength && GetIsServerMode(sntpData);
}

#region Nested type: State

private class State
{
public State(UdpClient socket, IPEndPoint endPoint, Action
getTime, Action failure)
{
Socket = socket;
EndPoint = endPoint;
GetTime = getTime;
Failure = failure;
}

public UdpClient Socket { get; private set; }

public Action GetTime { get; private set; }

public Action Failure { get; private set; }

public IPEndPoint EndPoint { get; private set; }
}

#endregion
}
}


Reply to this email directly or view it on GitHub
<
#12 (comment)

.


Reply to this email directly or view it on GitHub
#12 (comment)
.

@ayende
Copy link
Owner

ayende commented Aug 28, 2015

Thanks, I mreged it.

*Hibernating Rhinos Ltd *

Oren Eini* l CEO l *Mobile: + 972-52-548-6969

Office: +972-4-622-7811 *l *Fax: +972-153-4-622-7811

On Fri, Aug 28, 2015 at 8:46 PM, trailway [email protected] wrote:

Done.

My first pull request.

On Fri, Aug 28, 2015 at 11:07 AM, Ayende Rahien [email protected]
wrote:

I can't really read this. Can you send this as a PR, that way it would be
much easier to see the changes.

*Hibernating Rhinos Ltd *

Oren Eini* l CEO l *Mobile: + 972-52-548-6969

Office: +972-4-622-7811 *l *Fax: +972-153-4-622-7811

On Fri, Aug 28, 2015 at 4:54 PM, trailway [email protected]
wrote:

See attached code.

On Fri, Aug 28, 2015 at 5:53 AM, Ayende Rahien <
[email protected]

wrote:

Can you show your changes? I'm not sure that I can follow from the
description.


Reply to this email directly or view it on GitHub
<

#12 (comment)

.

using System;
using System.CodeDom.Compiler;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace LicensingShared
{
///

/// Provides support for asynchronously acquiring the current time
/// via well known sources for time.
///

///
/// This code is taken from rhino-licensing.
/// See copyright
notice.
/// Minor corrections where made to the code as a result of testing.
///
[GeneratedCode(@"rhino-licensing", @"08/25/2015")]
internal class SntpClient
{
private const byte SntpDataLength = 48;
private readonly string[] _hosts;
private int _index = -1;

public SntpClient(string[] hosts)
{
_hosts = hosts;
}

private static bool GetIsServerMode(byte[] sntpData)
{
return (sntpData[0] & 0x7) == 4 /* server mode */;
}

private static DateTime GetTransmitTimestamp(byte[] sntpData)
{
var milliSeconds = GetMilliSeconds(sntpData, 40);
return ComputeDate(milliSeconds);
}

private static DateTime ComputeDate(ulong milliseconds)
{
return new DateTime(1900, 1,
1).Add(TimeSpan.FromMilliseconds(milliseconds));
}

private static ulong GetMilliSeconds(byte[] sntpData, byte offset)
{
ulong intpart = 0, fractpart = 0;

for (var i = 0; i <= 3; i++)
{
intpart = 256 * intpart + sntpData[offset + i];
}
for (var i = 4; i <= 7; i++)
{
fractpart = 256 * fractpart + sntpData[offset + i];
}
var milliseconds = intpart * 1000 + (fractpart * 1000) / 0x100000000L;
return milliseconds;
}

public void BeginGetDate(Action getTime, Action failure)
{
_index += 1;
if (_hosts.Length <= _index)
{
if (_hosts.Length == _index)
{
failure();
}
return;
}
try
{
var host = _hosts[_index];
var state = new State(null, null, getTime, failure);
var result = Dns.BeginGetHostAddresses(host, EndGetHostAddress, state);
RegisterWaitForTimeout(state, result);
}
catch (Exception)
{
// retry, recursion stops at the end of the hosts
BeginGetDate(getTime, failure);
}
}

private void EndGetHostAddress(IAsyncResult asyncResult)
{
var state = (State)asyncResult.AsyncState;
try
{
var addresses = Dns.EndGetHostAddresses(asyncResult);
var endPoint = new IPEndPoint(addresses[0], 123);

var socket = new UdpClient();
socket.Connect(endPoint);
socket.Client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.ReceiveTimeout, 500);
socket.Client.SetSocketOption(SocketOptionLevel.Socket,
SocketOptionName.SendTimeout, 500);
var sntpData = new byte[SntpDataLength];
sntpData[0] = 0x1B; // version = 4 & mode = 3 (client)

var newState = new State(socket, endPoint, state.GetTime,
state.Failure);
var result = socket.BeginSend(sntpData, sntpData.Length, EndSend,
newState);
RegisterWaitForTimeout(newState, result);
}
catch (Exception)
{
// retry, recursion stops at the end of the hosts
BeginGetDate(state.GetTime, state.Failure);
}
}

private void RegisterWaitForTimeout(State newState, IAsyncResult
result)
{
if (result != null)
{
ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle,
MaybeOperationTimeout, newState, 500,
true);
}
}

private void MaybeOperationTimeout(object state, bool timedOut)
{
if (timedOut == false)
return;

var theState = (State)state;
try
{
theState.Socket?.Close();
}
catch (Exception)
{
// retry, recursion stops at the end of the hosts
BeginGetDate(theState.GetTime, theState.Failure);
}
}

private void EndSend(IAsyncResult ar)
{
var state = (State)ar.AsyncState;
try
{
state.Socket.EndSend(ar);
var result = state.Socket.BeginReceive(EndReceive, state);
RegisterWaitForTimeout(state, result);
}
catch
{
state.Socket.Close();
BeginGetDate(state.GetTime, state.Failure);
}
}

private void EndReceive(IAsyncResult ar)
{
var state = (State)ar.AsyncState;
try
{
var endPoint = state.EndPoint;
var sntpData = state.Socket.EndReceive(ar, ref endPoint);
if (IsResponseValid(sntpData) == false)
{
state.Failure();
return;
}
var transmitTimestamp = GetTransmitTimestamp(sntpData);
state.GetTime(transmitTimestamp);
}
catch
{
BeginGetDate(state.GetTime, state.Failure);
}
finally
{
state.Socket.Close();
}
}

private bool IsResponseValid(byte[] sntpData)
{
return sntpData.Length >= SntpDataLength && GetIsServerMode(sntpData);
}

#region Nested type: State

private class State
{
public State(UdpClient socket, IPEndPoint endPoint, Action
getTime, Action failure)
{
Socket = socket;
EndPoint = endPoint;
GetTime = getTime;
Failure = failure;
}

public UdpClient Socket { get; private set; }

public Action GetTime { get; private set; }

public Action Failure { get; private set; }

public IPEndPoint EndPoint { get; private set; }
}

#endregion
}
}


Reply to this email directly or view it on GitHub
<

#12 (comment)

.


Reply to this email directly or view it on GitHub
<
#12 (comment)

.


Reply to this email directly or view it on GitHub
#12 (comment)
.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants