1

This is a follow-up question to this question. On the next level, I now want to use maximal task concurrency to connect to expected hosts on a large set of IP addresses, using TCP/IP on a specific port.

My own research, as well as community reference, has lead me to key articles, for example:

This allowed me to set up my own code, which works fine, but currently takes a full 30 seconds to finish scanning 255 IPs, using only one specific port. Given the test, machine has 8 logical cores this observation suggests that my construct actually spawns at maximum 8 concurrent tasks (255/8=31.85).

The function I wrote returns a list of responding IPs {IPs} which is a subset of the List of all IPs {IP_Ports} to be checked. This is my current code, working fine but not yet suitable for use on larger networks due to what I suspect is lack of efficient task concurrency:

// Check remote host connectivity
public static class CheckRemoteHost
{

    // Private Class members
    private static bool AllDone     = false;
    private static object lockObj   = new object();
    private static List<string> IPs;


    // Wrapper: manage async method <TCP_check>
    public static List<string> TCP(Dictionary<string, int> IP_Ports, int TimeoutInMS = 100)
    {
        // Locals
        IPs = new List<string>();

        // Perform remote host check
        AllDone = false;
        TCP_check(IP_Ports, TimeoutInMS);
        while (!AllDone) { Thread.Sleep(50); }

        // Finish
        return IPs;
    }
    private static async void TCP_check(Dictionary<string, int> IP_Ports, int timeout)
    {// async worker method:  check remote host via TCP-IP


        // Build task-set for parallel IP queries
        var tasks = IP_Ports.Select(host => TCP_IPAndUpdateAsync(host.Key, host.Value, timeout));

        // Start execution queue
        await Task.WhenAll(tasks).ContinueWith(t =>
        {
            AllDone = true;
        });
    }
    private static async Task TCP_IPAndUpdateAsync(string ip, int port, int timeout)
    {// method to call IP-check

        // Run method asynchronously
        await Task.Run(() =>
        {
            // Locals
            TcpClient client;
            IAsyncResult result;
            bool success;

            try
            {
                client  = new TcpClient();
                result  = client.BeginConnect(ip, port, null, null);
                success = result.AsyncWaitHandle.WaitOne(TimeSpan.FromMilliseconds(timeout));

                if (success)
                {
                    lock (lockObj)
                    {
                        IPs.Add(ip);
                    }
                }
            }
            catch (Exception e)
            {
                // do nothing
            }
        });
    }


}// end     public static class CheckRemoteHost

So my question is: how can I maximize the task concurrency of requesting a response using TCP/IP at Port X such that I can obtain very fast IP-Port network scans on large internal networks?

supersausage007
  • 93
  • 1
  • 10

1 Answers1

0

Details

The default task scheduler is usually the ThreadPool scheduler. That means the number of concurrent tasks will be limited by the available threads in the pool.

Remarks

The thread pool provides new worker threads or I/O completion threads on demand until it reaches the minimum for each category. By default, the minimum number of threads is set to the number of processors on a system. When the minimum is reached, the thread pool can create additional threads in that category or wait until some tasks complete. Beginning with the .NET Framework 4, the thread pool creates and destroys threads in order to optimize throughput, which is defined as the number of tasks that complete per unit of time. Too few threads might not make optimal use of available resources, whereas too many threads could increase resource contention.

(Source: https://msdn.microsoft.com/en-us/library/system.threading.threadpool.getminthreads(v=vs.110).aspx)

You are likely just under the threshold where the threadpool would spin up new threads since tasks are being completed. Hence why you only have 8 concurrent tasks running at once.

Solutions

1. Use ConnectAsync with a timeout.

Instead of creating a separate task which blocks waiting for the connect. You can call ConnectAsync and join it with a delay to create a timeout. ConnectAsync doesn't seem to block the threadpool threads.

public static async Task<bool> ConnectAsyncWithTimeout(this Socket socket, string host, int port, int timeout = 0)
{
    if (timeout < 0)
        throw new ArgumentOutOfRangeException("timeout");
    try
    {
        var connectTask = socket.ConnectAsync(host, port);
        var res = await Task.WhenAny(connectTask, Task.Delay(timeout));
        await res;
        return connectTask == res && connectTask.IsCompleted && !connectTask.IsFaulted;
    } 
    catch(SocketException se)
    {
        return false;
    }
}

Example usage

private static async Task TCP_IPAndUpdateAsync(string ip, int port, int timeout)
{// method to call IP-check
    client  = new TcpClient();
    var success = await client.Client.ConnectAsyncWithTimeout(ip, port, timeout);
    if (success)
    {
        lock (lockObj)
        {
            IPs.Add(ip);
        }
    }
}

2. Use long running tasks.

Using Task.Factor.StartNew you can specify that the task is LongRunning. The threadpool task scheduler specifically will create a new thread for the task instead of using the threadpool. This will get around the 8 thread limit you are hitting. However, it should be noted that this is not a good solution if you plan to naively create thousands of tasks. Since at that point, the bottle neck will be thread context switches. You could however split all of the work between, for example, 100 tasks.

3. Use non-blocking connect

This method doesn't require creating multiple tasks. Instead you can call multiple connects on a single thread and check the status of multiple sockets at once. This method is a bit more involved though. If you rather go with this approach and want a more complete example then comment letting me know. Here is a quick snippet on how to use the API.

var socket = new Socket(SocketType.Stream, ProtocolType.Tcp);
socket.Blocking = false;
try
{
    socket.Connect("127.0.0.1", 12345);
} 
catch(SocketException se)
{
    //Ignore the "A non-blocking socket operation could not be completed immediately" error
    if (se.ErrorCode != 10035)
        throw;
}

//Check the connection status of the socket.
var writeCheck = new List<Socket>() { socket };
var errorCheck = new List<Socket>() { socket };
Socket.Select(null, writeCheck, errorCheck, 0);

if (writeCheck.Contains(socket)) 
{
    //Connection opened successfully.
}
else if (errorCheck.Contains(socket))
{
    //Connection refused
}
else
{
    //Connect still pending
}
Community
  • 1
  • 1
Will
  • 10,013
  • 9
  • 45
  • 77
  • thank you for that. I am trying to test the code; in Example 1 method is rejected by the compiler; the call socket.ConnectAsync(host, port) requires a function signature , I tried quickly to adapt your code in order to get it to run, but it sent me down a rabbit hole. Any ideas what I might not be seeing? – supersausage007 Jul 06 '18 at 11:49
  • @supersausage007 Ya I tacked on #1 last and didn't fully explain it. It's using an extension method from .net 4.7. https://msdn.microsoft.com/en-us/library/system.net.sockets.sockettaskextensions(v=vs.110).aspx . If you are using a .net version before 4.7 you can grab the source for that method from here. https://github.com/dotnet/corefx/blob/master/src/System.Net.Sockets/src/System/Net/Sockets/SocketTaskExtensions.netfx.cs#L60 – Will Jul 06 '18 at 14:24