У меня есть DataGrid, который отображает результаты сканирования:
Код: Выделить всё
Код: Выделить всё
public class NetScanResult : ObservableObject
{
private IPAddress ipAddress;
public IPAddress IpAddress
{
get => ipAddress;
set
{
SetProperty(ref ipAddress, value);
OnPropertyChanged(nameof(IpAddressStr));
}
}
private ObservableCollection openPorts;
public ObservableCollection OpenPorts
{
get => openPorts;
set { SetProperty(ref openPorts, value); }
}
public NetScanResult()
{
OpenPorts = new ObservableCollection();
}
}
Код: Выделить всё
[RelayCommand]
public async void NetScan2Start()
{
try
{
int[] ports = { 22, 80, 443, 554, 3389, 8000, 37777 };
int timeoutMs = 2500;
var stopwatch = new Stopwatch();
stopwatch.Start();
var netScanResults = await NetUtility.ScanNetwork2_Prepare(NetScanIpRangeBegin, NetScanIpRangeEnd, NetScanNetmaskSelected, ports, timeoutMs);
NetScanResults = new ObservableCollection(netScanResults);
var tasks = new List();
foreach (var netScanResult in NetScanResults)
{
foreach (var port in ports)
{
NetScanResult tempResult = netScanResult;
int tempPort = port;
var task = ScanPortAsync(tempResult, tempPort, timeoutMs);
tasks.Add(task);
}
}
await Task.WhenAll(tasks.ToArray());
stopwatch.Stop();
MessageBox.Show($"Scan finished in {stopwatch.ElapsedMilliseconds}ms");
}
catch (Exception ex)
{
ShowInvalidData(ex.Message);
return;
}
}
private async Task ScanPortAsync(NetScanResult result, int port, int timeoutMs)
{
try
{
Socket socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
var connectResult = socket.BeginConnect(result.IpAddress, port, null, null);
await Task.Run(() => { connectResult.AsyncWaitHandle.WaitOne(timeoutMs, true); });
if (socket.Connected)
{
socket.EndConnect(connectResult);
Application.Current.Dispatcher.Invoke(() =>
{
result.OpenPorts.Add(port);
});
Debug.WriteLine($"{result.IpAddress}:{port} - open.");
}
socket.Close();
}
catch (Exception ex)
{
Debug.WriteLine($" Exception happened =( - {ex.Message}");
}
}
Он размораживается, когда все потоки сканирования завершены.
Чего мне не хватает?
Обновление:
Я изменил свой код и реализовал ваши предложения.
Код: Выделить всё
private int progressStep;
private int progress;
private int progressTotal;
private DispatcherTimer progressTimer;
[RelayCommand]
public async void NetScan3Start() => await Task.Run(async () =>
{
int[] ports = { 22, 80, 443, 554, 3389, 8000, 37777 };
int timeoutMs = 250;
var stopwatch = new Stopwatch();
stopwatch.Start();
IList addresses = await NetUtility.ScanNetwork2_Prepare(NetScanIpRangeBegin, NetScanIpRangeEnd, NetScanNetmaskSelected);
//
var list = new List();
foreach (var address in addresses)
{
list.Add(new NetScanResult2(address));
}
progressTotal = list.Count * ports.Length;
progressStep = (int)(list.Count * ports.Length * 0.05); //5% of total amount;
progress = 0;
_ = Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
NetScanResults = new ObservableCollection(list);
}));
progressTimer = new DispatcherTimer(DispatcherPriority.Normal, Application.Current.Dispatcher);
progressTimer.Interval = TimeSpan.FromSeconds(1);
progressTimer.Tick += ProgressTimer_Tick;
progressTimer.Start();
Application.Current.Dispatcher.Thread.Priority = ThreadPriority.Highest;
//
var scanTasks = new List();
var semaphore = new SemaphoreSlim(5);
foreach (var netScanResult in list)
{
var tmpNetScanResult = netScanResult;
var tmpAddress = netScanResult.IpAddress;
foreach (var port in ports)
{
var tmpPort = port;
var scanTask = Task.Run(async () =>
{
await semaphore.WaitAsync();
try
{
var client = new TcpClient();
var connectTask = client.ConnectAsync(tmpAddress, tmpPort);
var delayTask = Task.Delay(timeoutMs);
var result = await Task.WhenAny(connectTask, delayTask);
if (result == connectTask && client.Connected)
{
_ = Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
tmpNetScanResult.OpenPorts.Add(tmpPort);
}));
}
client.Dispose();
}
catch (SocketException)
{
}
catch (Exception ex)
{
}
finally
{
++progress;
semaphore.Release();
}
});
scanTasks.Add(scanTask);
}
}
await Task.WhenAll(scanTasks);
stopwatch.Stop();
progressTimer.Stop();
MessageBox.Show($"Finished in {stopwatch.ElapsedMilliseconds}ms");
});
private void ProgressTimer_Tick(object? sender, EventArgs e)
{
NetScanAmount = progressTotal - progress;
}
Добавление семафора для ограничения количества одновременных задач сканирования до 5 и переход на Dispatcher.BeginInvoke для обновлений пользовательского интерфейса несколько уменьшил проблемы зависания пользовательского интерфейса, но не полностью. Однако это также значительно увеличило время, необходимое для завершения всех сканирований.
Я также вижу много исключений: «System.Net.Sockets.SocketException» в System.Net. .Sockets.dll и исключение: «System.Net.Sockets.SocketException» в System.Private.CoreLib.dll в выводе отладки, что странно, поскольку есть try-catch заблокируйте метод ConnectAsync — я подумал, что, возможно, эти исключения также могут замедлить работу приложения.
Если я удалю семафор или увеличу его ограничение с 5 до 7 или большего числа, пользовательский интерфейс практически не отвечает на запросы, но иногда ему удается отображать некоторые обновления.
Я запускаю это на процессоре Intel i5-1135g7, если это имеет значение.
Все еще пытаюсь понять причину зависаний пользовательского интерфейса.
Подробнее здесь: https://stackoverflow.com/questions/789 ... sync-tasks