Robust asynchronous connection to the rarely available TCP server

EDN Admin

Well-known member
Joined
Aug 7, 2010
Messages
12,794
Location
In the Machine
<span style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px There is a “crafty” server that is available (responds on ping requests) and
simultaneously<span style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px accepts TCP connections at the random time intervals. I need to write a non-blocking function on C# (in .NET 4.5) that
<span style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px tries to get successfully connection with this server<span style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px . It gets
two arguments: http://msdn.microsoft.com/en-us/library/system.net.ipendpoint.aspx " style="margin:0px; padding:0px; border:0px; font-size:14px; vertical-align:baseline; color:#4a6b82; text-decoration:initial; font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif IPEndPoint <span style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px and
Action<Socket> (socket handler for successful connections) or Action< http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx " style="margin:0px; padding:0px; border:0px; font-size:14px; vertical-align:baseline; color:#4a6b82; text-decoration:initial; font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif SocketAsyncEventArgs <span style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px >
and returns a http://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken.aspx " style="margin:0px; padding:0px; border:0px; font-size:14px; vertical-align:baseline; color:#4a6b82; text-decoration:initial; font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif CancellationToken <span style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px .<br style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px
<span style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px I would like to write this procedure in a neatly manner (e.g. using http://msdn.microsoft.com/en-us/devlabs/gg585582.aspx " style="margin:0px; padding:0px; border:0px; font-size:14px; vertical-align:baseline; color:#4a6b82; text-decoration:initial; font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif TDF <span style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px or http://msdn.microsoft.com/en-us/data/gg577609.aspx " style="margin:0px; padding:0px; border:0px; font-size:14px; vertical-align:baseline; color:#4a6b82; text-decoration:initial; font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif Rx <span style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px ).
Im quite new in Reactive Extensions, Task Parallel Library and Task Data Flow, so I would like to get code examples and detailed comments.
<span style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px Here is my current code:
<pre class="prettyprint" style=" public partial class NetworkAgent
{
public static readonly Ping Pinguin = new Ping();
public static readonly TimeSpan PING_TIMEOUT = TimeSpan.FromMilliseconds(200);

public static bool IsAvailableNow(IPAddress ip)
{
try
{
var pingResult = Pinguin.Send(ip, (int)PING_TIMEOUT.TotalMilliseconds, new byte[0]);
return pingResult.Status == IPStatus.Success;
}
catch
{
return false;
}
}

protected class ConnectionToken
{
public CancellationTokenSource CTS;
public Action<ConnectAsyncEventArgs> Callback;
public int Age = 0;
}

static NetworkAgent()
{
var p = new Thread(() => Connector())
{
Priority = ThreadPriority.Highest,
IsBackground = true
};
p.Start();
}

protected static BlockingCollection<SocketAsyncEventArgs> ConnectionWorkersQueue
= new BlockingCollection<SocketAsyncEventArgs>();

protected static void Connector()
{
while (true)
{
try
{
StartConnect(ConnectionWorkersQueue.Take());
Thread.Sleep(50);
}
catch (Exception e)
{
LogException(e);
}
}
}

public static CancellationTokenSource ConnectAsync(IPEndPoint remoteEndPoint, Action<ConnectAsyncEventArgs> callback)
{
var connectEventArg = new SocketAsyncEventArgs()
{
RemoteEndPoint = remoteEndPoint
};

var cts = new CancellationTokenSource();
cts.Token.Register(() => CancelConnectAsync(connectEventArg));
var ut = new ConnectionToken()
{
CTS = cts,
Callback = callback
};
connectEventArg.UserToken = ut;

connectEventArg.Completed += new EventHandler<SocketAsyncEventArgs>((_, e) => ConnectAsyncEventsHandler(e));
ConnectionWorkersQueue.Add(connectEventArg); // StartConnect(connectEventArg);
return ut.CTS;
}

private static void CancelConnectAsync(SocketAsyncEventArgs connectEventArg)
{
Socket.CancelConnectAsync(connectEventArg);
}

private static void StartConnect(SocketAsyncEventArgs connectEventArg)
{
var ip = ((IPEndPoint)connectEventArg.RemoteEndPoint).Address;
var ut = (ConnectionToken)connectEventArg.UserToken;
if (!ut.CTS.IsCancellationRequested)
{
ut.Age++;
ut.Callback(new ConnectAsyncEventArgs() { Context = ConnectAsyncEvents.Ping, Age = ut.Age });
if (!IsAvailableNow(ip) || !(IsAvailableNow(ip) && IsAvailableNow(ip)))
{
ConnectionWorkersQueue.Add(connectEventArg);
}
else
{
ut.Callback(new ConnectAsyncEventArgs() { Context = ConnectAsyncEvents.Binding, Age = ut.Age });
try
{
if (!Socket.ConnectAsync(SocketType.Stream, ProtocolType.Tcp, connectEventArg))
{
ConnectAsyncEventsHandler(connectEventArg);
}
}
catch (Exception e)
{
LogException(e);
}
}
}
}

static void ConnectAsyncEventsHandler(SocketAsyncEventArgs e)
{
var ut = (ConnectionToken)e.UserToken;
if (!ut.CTS.IsCancellationRequested)
{
switch (e.SocketError)
{
case SocketError.Success:
ut.Callback(new ConnectAsyncEventArgs() { Context = ConnectAsyncEvents.Done, Age = ut.Age, BoundSocket = e.ConnectSocket });
break;
case SocketError.ConnectionRefused:
ConnectionWorkersQueue.Add(e);
break;
case SocketError.TimedOut:
ConnectionWorkersQueue.Add(e);
break;
default:
Debug.WriteLine("Unhandled connection error: " + e.SocketError);
break;
}
}
}
}[/code]
<br/>
<br/>

<span style="font-family:Arial,Liberation Sans,DejaVu Sans,sans-serif; font-size:14px
<br/>
<br/>

View the full article
 
Back
Top