我的代码从给定主机获取开放端口。 这个过程非常耗时
代码:
/
此过程对我来说需要 11s ! 如果我检查100或1000个端口,这是非常长的......
任何好& 快速这样做的方法?
答案 0 :(得分:5)
您不需要多个线程即可连接到多个端口。您可以使用ConnectAsync异步连接 。 Windows通过NT天的完成端口使用异步IO。
这意味着没有线程被阻止,操作系统在IO请求完成时通知原始线程。实际上,阻塞是模拟以使同步编程更容易。
您可以创建一个连接到单个端口的方法,并在完成后报告消息。尽管写入控制台块,因此我将使用IProgress< T>界面报告进度:
public async Task ConnectToPortAsync(string host,int port,IProgress<string> progress)
{
using(var client=new TcpClient())
{
try
{
await client.ConnectAsync(host,port).ConfigureAwait(false);
progress.Report($"Port: {port} Open");
//Do some more work
}
catch
{
progress.Report($"Port {port} Closed");
}
}
}
假设您有一个端口列表:
var ports=new[]{80,8080,...};
或
var ports=Enumerable.Range(0,11);
您可以同时调用多个端口:
var host="127.0.0.1";
var progress=new Progress<string>(msg=>Console.WriteLine(msg););
var allTasks= ports.Select(port=>ConnectToPortAsync(host,port,progress));
await Task.WhenAll(allTasks);
此代码将使用主线程直到第一个await
。这意味着将使用主线程创建所有TcpClient
个实例。在那之后,代码将在必要时使用IO Completion线程池中的线程异步执行。
报告由主线程由Progress< T>对象执行,因此后台线程不会阻止等待写入控制台。
最后,一旦await
返回,就会继续执行原始同步上下文。对于将成为主线程的桌面应用程序(WPF,Winforms)。如果要在异步操作后更新UI,则可以,但如果要在后台执行更多工作,则可能导致阻塞。通过使用ConfigureAwait(false)
,我们指示运行时继续处理后台线程
进一步改进:
您可以使用Task.Delay()和Task.WhenAny添加超时,以防连接时间过长:
var connectionTask=client.ConnectAsync(host,port);
var timeout=Task.Delay(1000);
var completed=await Task.WhenAny(connectionTask,timeout);
if(completed==connectionTask)
{
progress.Report($"Port {port} Open");
}
else
{
progress.Report($"Port {port} Timeout");
}
答案 1 :(得分:3)
如果您要问的是如何并行运行多个查找,请查看Parallel
类。
Parallel.For(0, 11, new ParallelOptions { MaxDegreeOfParallelism = 5 }, (port) =>
{
string statusTCP = "Open";
using (TcpClient tcp = new TcpClient())
{
try
{
tcp.Connect("127.0.0.1", port);
}
catch { statusTCP = "Close"; }
}
Console.WriteLine("Port " + port + " : " + statusTCP);
});
注意我在前几个方法参数中指定从0到10(因为11是独占的)并且与ParallelOptions
类并行执行5次查找。