Stack Overflow на русском Asked by Zekoy on December 15, 2020
// работа на тему Информационная безопасность
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp3
{
class Program
{
private static string IP = "";
static void Main(string[] args)
{
Console.Title = "PortScanner";
UserInput();
PortScan();
Console.ReadKey();
}
private static void UserInput()
{
Console.Write("IP Address:", ConsoleColor.White);
IP = Console.ReadLine();
}
private static void PortScan()
{
Console.Clear();
TcpClient Scan = new TcpClient();
foreach (int s in Ports)
{
try
{
Scan.Connect(IP, s);
Console.WriteLine($"[{s}] | OPEN", ConsoleColor.Green);
}
catch
{
Console.WriteLine($"[{s}] | CLOSED", ConsoleColor.Red);
}
}
}
private static int[] Ports = new int[]
{
1,
2,
3,
80,
};
}
}
Очень долго и длительно сканирует порты на указаном IP. Как ускорить процесс?
Как указать порты одной строкой? (глупо выходит их вот так вводить через запятую)
Если очень просто, то ответ такой: Используйте асинхронность, не ждите выполнение другой проверки!
В вашем коде надо сделать пару вещей:
Main
сделать с void
на async Task
.Task
), которая будет проверять один порт.
PortScan
выкидываем все лишнее (цикл, очистку консоли)void
на async Task
..Connect()
на .ConnectAsync()
, добавив в начало await
.TcpClient
оборачиваем в using
, ибо его надо закрывать!Task.WhenAll()
.В итоге получиться нечто такое:
private static string IP = "";
static async Task Main(string[] args)
{
Console.Title = "PortScanner";
UserInput();
Console.Clear();
var tasks = Ports.Select(x => PortScan(x));
await Task.WhenAll(tasks);
Console.ReadKey();
}
private static void UserInput()
{
Console.Write("IP Address:", ConsoleColor.White);
IP = Console.ReadLine();
}
private static async Task PortScan(int port)
{
using TcpClient Scan = new TcpClient();
try
{
await Scan.ConnectAsync(IP, port);
Console.WriteLine($"[{port}] | OPEN", ConsoleColor.Green);
}
catch
{
Console.WriteLine($"[{port}] | CLOSED", ConsoleColor.Red);
}
}
private static int[] Ports = new int[]
{
1,
2,
3,
80,
};
Теперь у вас проверка портов пойдет асинхронно, то есть без ожидания завершения предыдущей проверки.
Но, у вас есть ряд моментов, которые можно улучшить, давайте сделаем это:
UserInput()
- Почему этот метод не отдает результат, а записывает в некую статичную переменную? Ведь его задача, это спросить пользователя и отдать нам для работы его ответ. Давайте напишем чуть по-другому:
static string AskUser(string message)
{
Write($"{message}: ");
return ReadLine();
}
Теперь метод отдает нам то, что ввел пользователь.
Вывод цветного сообщения - тут стоит тоже сделать удобный метод, например такой:
static void ColoredText(string title, string text, ConsoleColor color)
{
Write($"{title}: ");
ForegroundColor = color;
WriteLine(text);
ResetColor();
}
Теперь можно удобно вывести текст, допустим, статуса порта.
IP - Научитесь использовать правильные типы, а не некие базовые. Для ip в c# есть специальный класс IPAddress
, работайте с ним! Давайте сделаем метод, который будет спрашивать пользователя ip, если он некорректен, то просит по новой, а в случае успешного ввода, отдаст нам IPAddress
:
static IPAddress AskIp(string message)
{
while (true)
{
var reply = AskUser(message);
if (IPAddress.TryParse(reply, out var ip))
{
return ip;
}
else
{
ColoredText(reply, "Не является IP", ConsoleColor.Red);
ReadKey();
Clear();
}
}
}
Улучшим метод сканирования - сейчас задача ждет таймаута, это тоже можно изменить:
Сделаем отдельный метод, (некую обертку над клиентом, которую по-хорошему лучше засунуть в отдельный класс), который будет пытаться сделать соединение, но также асинхронно будет запущен некий таймер, который выдаст ошибку, если выполниться он быстрее:
static async Task TimeoutConnectAsync(IPAddress ip, int port, TimeSpan connectTimeout = default)
{
if (connectTimeout == default)
connectTimeout = TimeSpan.FromMilliseconds(500);
using var client = new TcpClient();
var cancelTask = Task.Delay(connectTimeout);
var connectTask = client.ConnectAsync(ip, port);
await Task.WhenAny(connectTask, cancelTask);
if (cancelTask.IsCompleted)
{
throw new TimeoutException();
}
}
Ну и сама задача проверки одного порта:
static async Task CheckPortAsync(IPAddress ip, int port, TimeSpan connectTimeout = default)
{
bool result;
try
{
await TimeoutConnectAsync(ip, port, connectTimeout);
result = true;
}
catch
{
result = false;
}
ColoredText($"{ip}:{port}", result ? "Открыт" : "Закрыт", result ? ConsoleColor.Green : ConsoleColor.Red);
}
Теперь при проверке порта, если он не доступен в течении 500ms, мы считаем его "закрытым".
Множественная проверка - вам не нужно инициализировать новый массив с портами, достаточно воспользоваться params
и передавать в метод все порты через запятую. Давайте сделаем новый метод, который будет принимать список портов, ну и проверит их все разом:
static async Task CheckPortsAsync(IPAddress ip, params int[] ports)
{
var tasks = ports.Select(x => CheckPortAsync(ip, x));
await Task.WhenAll(tasks);
}
Вызывается это просто, await CheckPortsAsync(ip, 1, 2, 3, 80);
Я тут не стал говорить про ООП и прочие особенности, просто знайте, что в одном классе это писать не хорошо, разбейте все это по разным классам, которые будут отвечать за что-то одно.
Весь код:
static async Task Main()
{
var ip = AskIp("IP Address");
await CheckPortsAsync(ip, 1, 2, 3, 80);
}
static async Task CheckPortAsync(IPAddress ip, int port, TimeSpan connectTimeout = default)
{
bool result;
try
{
await TimeoutConnectAsync(ip, port, connectTimeout);
result = true;
}
catch
{
result = false;
}
ColoredText($"{ip}:{port}", result ? "Открыт" : "Закрыт", result ? ConsoleColor.Green : ConsoleColor.Red);
}
static async Task CheckPortsAsync(IPAddress ip, params int[] ports)
{
var tasks = ports.Select(x => CheckPortAsync(ip, x));
await Task.WhenAll(tasks);
}
static async Task TimeoutConnectAsync(IPAddress ip, int port, TimeSpan connectTimeout = default)
{
if (connectTimeout == default)
connectTimeout = TimeSpan.FromMilliseconds(500);
using var client = new TcpClient();
var cancelTask = Task.Delay(connectTimeout);
var connectTask = client.ConnectAsync(ip, port);
await Task.WhenAny(connectTask, cancelTask);
if (cancelTask.IsCompleted)
{
throw new TimeoutException();
}
}
static void ColoredText(string title, string text, ConsoleColor color)
{
Write($"{title}: ");
ForegroundColor = color;
WriteLine(text);
ResetColor();
}
static string AskUser(string message)
{
Write($"{message}: ");
return ReadLine();
}
static IPAddress AskIp(string message)
{
while (true)
{
var reply = AskUser(message);
if (IPAddress.TryParse(reply, out var ip))
{
return ip;
}
else
{
ColoredText(reply, "Не является IP", ConsoleColor.Red);
ReadKey();
Clear();
}
}
}
Результат выполнения:
Вот, собственно, и все, направление я вам показал, остальное за вами!
ReadLine()
, а не Console.ReadLine()
? Потому что мне так удобней) Делается при помощи using static System.Console;
.lock
и прочими приблудами. Ну а вообще, по-хорошему, лучше делать Task<bool>
и уже после того, как все задачи завершились, выводить результат. Метод проверки не обязан выводить что либо, это, по сути, нарушение SOLID, так что подумайте об этом!можно что-то оригинальное придумать, типа 1-80, 123, 443, 500-2000
Если нужно такое, то можно написать примерно следующий метод:
private static IEnumerable<int> ParseRange(string range) =>
range.Split(',')
.Select(x => x.Split('-'))
.Select(x => (First: int.Parse(x.First()), Last: int.Parse(x.Last())))
.SelectMany(x => Enumerable.Range(x.First, x.Last - x.First + 1))
.OrderBy(x => x);
Тут, по сути, все просто:
-
, что даст нам, по сути, коллекцию, состоящую из коллекций строк.(First: 1, Last: 2)
- это кортеж (можно взять и анонимный тип (new {First: 1, Last: 2}
).Enumerable.Range()
, который даст числа в указанном диапазоне.Вызываем:
var ports = ParseRange("1, 5-10, 13, 80, 100-100, 115-120")
Результат:
1
5
6
7
8
9
10
13
80
100
115
116
117
118
119
120
Correct answer by EvgeniyZ on December 15, 2020
Get help from others!
Recent Answers
Recent Questions
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP