2017-12-20 19 views
-2

私は、C#で単純なポートスキャナを作成しています。これは、IP範囲で与えられたオープンポートをスキャンすることになっています。以下から分かるように、私は、コードの大部分にasync/awaitTaskを使用しています:単一タスク対リストC#の<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 

、基本的にスキャンしていないようです開いているポートのいずれも印刷しないので、それぞれ別々に表示されます。何が問題なのか、そしてTaskList<Task>の中に複数回呼び出すにはどうすればいいですか?

+4

あなたはすべきですあなたの質問を削除して再投稿しないでください:https://stackoverflow.com/questions/47912486/data-parallelism-and-threads-in-c-sharp – Servy

+1

@Servyおそらく私は何かに問題を絞り込んでいるでしょう。それをより明示的にした。 – typos

+1

あなたの質問を明確にしたいのであれば、あなたの質問を編集していて、それを削除して再投稿する必要はありません。 – Servy

答えて

1

これは古典的なトラップで、Task.Factory.StartNewです。このメソッドはTask<T>を返します。Tがコールバックの戻り値の型です。あなたのラムダは非同期なので、タスクを返します。まとめると、Task<Task>になります。内側のタスクを待つ間に外側のタスクを待っています。

2種類の溶液:

  1. 推奨1: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(); 
    })); 
    
  2. あなたは、あなたが内側のいずれかに待つことができるようにタスクに.Unwrap()メソッドを使用することができ、Task.Factory.StartNewを使用する正当な理由がある場合:

    scanTasks.Add(Task.Factory.StartNew(async() => 
    { 
        PortScanner ps = new PortScanner(ip, 15, 25); 
        await ps.Scan(); 
    }).Unwrap()); 
    
+0

ありがとうございます。これは問題を解決し、今問題の内容を理解しました。 'Task.Factory.StartNew'が適切な選択であるような特別なガイドラインはありますか? – typos

+0

@typosまあ、短い話です:もしあなたが 'Task.Run'でできないことをしたいのなら(あなた自身が疑問を抱くなら' Task.Factory.StartNew'は必要ありません。 )。具体的には、カスタムタスクスケジューラやタスクのフラグ( 'LongRunning'や' PreferFairness'など)を設定することができます –

関連する問題