我在C#中创建一个简单的端口扫描程序,它应该扫描给定IP范围的开放端口。我在代码的大部分内容中使用async/await
和Task
,如下所示:
internal class PortScanner
{
private IPAddress host;
private int startPort;
private int endPort;
private const int PORT_MIN_VALUE = 1;
private const int PORT_MAX_VALUE = 65535;
public PortScanner(IPAddress host, int portStart, int portStop)
{
this.host = host;
this.startPort = portStart;
this.endPort = portStop;
}
public PortScanner(IPAddress host)
: this(host, PORT_MIN_VALUE, PORT_MIN_VALUE)
{
}
private async Task<bool> IsPortOpen(int port)
{
Socket socket = null;
try
{
// make a TCP based socket
socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
// connect
await Task.Run(() => socket.Connect(this.host, port));
return true;
}
catch (SocketException ex)
{
if (ex.SocketErrorCode == SocketError.ConnectionRefused)
{
return false;
}
}
finally
{
if (socket?.Connected ?? false)
{
socket?.Disconnect(false);
}
socket?.Close();
}
return false;
}
private async Task CheckPort(int port)
{
if (await IsPortOpen(port))
{
Console.WriteLine("Host: {0} - Port: {1} is open.", this.host.ToString(), port.ToString());
}
}
public async Task Scan()
{
Console.WriteLine("Scanning for {0}", this.host);
for (int port = this.startPort; port <= this.endPort; port++)
{
await CheckPort(port);
}
}
}
现在,重点是如果在Main
函数中,我使用单Task
这样:
try
{
PortScanner ps = new PortScanner(ipsInRange.Begin, 15, 25);
var task = ps.Scan();
task.Wait();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
它工作正常,从某种意义上说我得到了一个输出(并且需要几秒钟才能完成):
Scanning for 192.168.1.1
Host: 192.168.1.1 - Port: 21 is open.
Host: 192.168.1.1 - Port: 23 is open.
但是,当我尝试使用List<Task>
时,例如:
foreach (var set in setOfIPs)
{
List<Task> scanTasks = new List<Task>(set.Count());
foreach (var ip in set)
{
scanTasks.Add(Task.Factory.StartNew(async () =>
{
PortScanner ps = new PortScanner(ip, 15, 25);
await ps.Scan();
}));
}
Task.WaitAll(scanTasks.ToArray());
}
我得到的输出就是这个(程序也会在1秒内完成):
Scanning for 192.168.1.3
Scanning for 192.168.1.2
Scanning for 192.168.1.1
Scanning for 192.168.1.4
Scanning for 192.168.1.5
因此,它基本上似乎不会分别扫描它们,因为它不打印任何开放端口。任何想法可能是什么问题,如何在Task
内调用多个List<Task>
?
答案 0 :(得分:1)
这是Task.Factory.StartNew
的经典陷阱。该方法返回Task<T>
,其中T
是回调的返回类型。您的lambda是异步的,因此返回一个Task。放在一起,你最终会得到一个Task<Task>
,当你等待内部任务时,你正在等待外部任务。
两种解决方案:
推荐的:使用Task.Run
代替Task.Factory.StartNew
。这是一般性建议,除非您知道它是正确的解决方案,否则您不应该使用Task.Factory.StartNew
。 Task.Run
避免了许多陷阱,例如你遇到过的陷阱。
scanTasks.Add(Task.Run(async () =>
{
PortScanner ps = new PortScanner(ip, 15, 25);
await ps.Scan();
}));
如果您有合理的理由使用Task.Factory.StartNew
,那么您可以在任务上使用.Unwrap()
方法,以便能够在内部等待:
scanTasks.Add(Task.Factory.StartNew(async () =>
{
PortScanner ps = new PortScanner(ip, 15, 25);
await ps.Scan();
}).Unwrap());