I'm writing an app in which I have to scan an ip (intranet) range and see if the particular ip corresponds to a specific url. For example let's say we have the url: http://<ip>:8080/x/y
and we want to see if we can find an active server running in the range 192.168.1.1 - 192.168.1.254. Obviously, the scan process should not block the UI.
So I created the following async method:
private List<AvailableServer> _availableServerList;
public List<AvailableServer> AvailableServerList
{
get { return _availableServerList;}
set { _availableServerList = value;}
}
private Mutex objLock = new Mutex(true, "MutexForScanningServers");
private long IPsChecked;
private List<string> allPossibleIPs;
CancellationTokenSource cts;
public async Task GetAvailableServers()
{
Dictionary<string, string> localIPs = LocalIPAddress(); //new Dictionary<string, string>();
allPossibleIPs = new List<string>();
Dictionary<string, CancellationToken> kvrList = new Dictionary<string, CancellationToken>();
cts = new CancellationTokenSource();
foreach (KeyValuePair<string, string> kvp in localIPs)
{
allPossibleIPs.AddRange(getIpRange(kvp.Key.ToString(), kvp.Value.ToString()));
}
foreach (string ip in allPossibleIPs)
{
kvrList.Add(ip, cts.Token);
}
AvailableServerList = new List<AvailableServer>();
var downloads = kvrList.Select(kvr => isServerAvailableAsync(kvr));
Task[] dTasks = downloads.ToArray();
await Task.WhenAll(dTasks).ConfigureAwait(false);
}
whose purpose is to start a banch of Tasks, each one of them trying to receive a valid HttpClient request. Let's say the request is valid if the HttpClient.GetStringAsync does not throw any exception:
private async Task isServerAvailableAsync(Object obj)
{
using (var client = new HttpClient())
{
try
{
KeyValuePair<string, CancellationToken> kvrObj = (KeyValuePair<string, CancellationToken>)obj;
string urlToCheck = @"http://" + kvrObj.Key + ":8080/x/y";
string downloadTask = await client.GetStringAsync(urlToCheck).ConfigureAwait(false);
string serverHostName = Dns.GetHostEntry(kvrObj.Key).HostName;
AvailableServerList.Add(new AvailableServer(serverHostName, @"http://" + serverHostName + ":8080/x/y"));
// }
}
catch (System.Exception ex)
{
//...Do nothing for now...
}
finally
{
lock (objLock)
{
//kvrObj.Value.ThrowIfCancellationRequested();
IPsChecked++;
int tmpPercentage = (int)((IPsChecked * 100) / allPossibleIPs.Count);
if (IPsCheckedPercentageCompleted < tmpPercentage)
{
IPsCheckedPercentageCompleted = tmpPercentage;
OnScanAvailableServersStatusChanged(new EventArgs());
}
}
}
}
}
If the request is valid then we found an available server and so we add the url to our list. Else we catch an exception. Finally we update our percentage variables, because we want to pass them to our UI (xx% scanned). Each time a Task completes it fires a delegate which is used by our UI to get the new updated list of available servers and percentage completed. The main async function GetAvailableServers starts running via Task.Run(() => className.GetAvailableServers()) which exists inside a DelegateCommand which resides into a ViewModel of my own:
public ICommand GetAvailableServersFromViewModel
{
get
{
return new DelegateCommand
{
CanExecuteFunc = () => true,
CommandAction = () =>
{
Task.Run(() => utilsIp.GetAvailableServers());
}
};
}
}
The problem with my implementation is that the UI lags while scanning, which is easily seen through the loading spinner I have in my UI. Well the code is far from best and I know I am wrong somewhere.