Reputation: 474
My code gets open ports from the given host. This process is very time consuming
Code :
for(int port=0;port<11;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);
}
This process takes 11s for me ! This is very long if i check 100 or 1000 ports...
Any Good & Fast way to do this ?
Upvotes: 1
Views: 711
Reputation: 131189
You don't need multiple threads to connect to multiple ports. You can use ConnectAsync to connect asynchronously. Windows uses asynchronous IO through completion ports from the NT days.
This means that no threads are blocked, the OS notifies the original thread when an IO request completes. In fact, blocking is simulated to make synchronous programming easier.
You can create a single method that connects to a single port and reports a message when finished. Writing to the console blocks though, so I'll use the IProgress< T> interface to report progress:
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");
}
}
}
Assuming you have a list of ports:
var ports=new[]{80,8080,...};
or
var ports=Enumerable.Range(0,11);
You can call multiple ports simultaneously like this:
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);
This code will use the main thread up until the first await
. This means that all TcpClient
instances will be created using the main thread. After that though, the code will execute asynchronously, using a thread from the IO Completion thread pool whenever necessary.
Reporting is performed by the main thread by the Progress< T> object, so the background threads don't block waiting to write to the console.
Finally, once await
returns, execution continues on the original synchronization context. For a desktop application (WPF, Winforms) that would be the main thread. That's OK if you want to update the UI after an asynchronous operation, but can cause blocking if you want to perform more work in the background. By using ConfigureAwait(false)
we instruct the runtime to keep working on the background thread
Further improvements :
You can add a timeout in case a connection takes too long, by using Task.Delay() and 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");
}
Upvotes: 5
Reputation: 17271
If what you're asking is how to run multiple lookups in parallel, take a look at the Parallel
class.
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);
});
Note in the first few method parameters where I specify to go from 0-10 (because 11 is exclusive) and execute 5 lookups in parallel with the ParallelOptions
class.
Upvotes: 3