代码之家  ›  专栏  ›  技术社区  ›  Aximili

使用多线程/并行查找工作端口

  •  0
  • Aximili  · 技术社区  · 6 年前

    private int FindWorkingPort(int from, int to)
    {
        for (int port = from; port <= to; port++)
        {
            try
            {
                new FtpClient(_host, port).Connect();  // Instant on correct port, very slow on wrong port
                return port;
            }
            catch { }
        }
    
        throw new Exception("None of the port is working");
    }
    

    当第一次尝试是正确的,它是即时的。否则,每次尝试可能需要10秒钟。你可以想象如果正确的是最后一个。

    有没有办法让它同时尝试所有可能的端口(我不擅长多线程)

    编辑:

    我在用 FluentFTP .ConnectAsync()

    3 回复  |  直到 6 年前
        1
  •  -1
  •   Rishabh Kumar    6 年前

    您可以尝试使用多个线程同时连接到所有端口,但是随着更多线程的启动,性能会下降。我建议你试试这个任务。

        using System;
        using System.Threading.Tasks;
    
        public class Program
        {
            public class MyEventArgs : EventArgs
            {
                 public int port {get; set; }
    
                 public MyEventArgs(int port)
                 {
                     this.port = port;
                 }
            }
    
            delegate void OpenPort(object sender, MyEventArgs  e);
            event OpenPort OnOpen;
            int from = 10, to = 20;
            int Connected = 16;
    
            public static void Main()
            {
                var obj = new Program();
                // here you register your method ShowPort for OnOpen event. 
                obj.OnOpen += new OpenPort(ShowPort);
                for(int i = obj.from; i < obj.to; i++) {
                    obj.CheckPort(obj, i);
                }
            }
    
           private async void CheckPort(Program obj, int i) 
           {
               await CheckPortTask(obj, i);
           }
    
           private Task CheckPortTask(Program obj, int i)
           {
                 return Task.Run(() =>  {
                    if(i == obj.Connected)
                    {
                      obj.OnOpen(this, new MyEventArgs(i));
                    }
             });
           }            
    
            // This method will be called after connecting.
            public static void ShowPort(Object p, MyEventArgs args)
            {
                Console.WriteLine(args.port);
            }
        }
    

    如果要尝试使用线程,请替换任务。用以下代码运行上述代码中的块:

    new Thread(()=>{
                            if(i == obj.w) 
                            {
                                // this raises the event and calls the ShowPort method.
                                obj.OnOpen(obj, new MyEventArgs(i));
                            }
                        })).Start();
    
        2
  •  0
  •   FCin    6 年前

    这里不是最好的,但有效的解决方案:

    var tasks = Enumerable.Range(from, to - from).ToList().Select(port => {
        try
        {
            return new FtpClient(_host, port).ConnectAsync().ContinueWith(_ => (int?)port);
        }
        catch
        {
            return Task.FromResult((int?)null);
        }
    });
    var foundPort = (await Task.WhenAll(tasks)).FirstOrDefault(p => p != null);
    

    基本上,您可以创建一个要检查的所有端口的列表,并为每个端口启动一个任务。你要等到它们异步完成,然后得到第一个不同步的 null . 所有任务将并行运行,但是 await Task.WhenAll 将等待所有端口完成,这意味着它将始终等待~10秒(根据您的问题,检查不正确的端口通常需要~10秒)。

    Task.WhenAny ,但是当其他任务抛出异常时,返回正确的端口很棘手。

        3
  •  0
  •   FCin    6 年前

    这是另一种方法。当第一个有效任务完成时,应该立即返回:

    public async Task<int> FindWorkingPortAsync(int from, int to)
    {
        var wrapper = new TaskCompletionSource<int>();
        var tasks = Enumerable.Range(from, to - from).ToList().Select(port =>
        {
            return new FtpClient(_host, port).ConnectAsync().ContinueWith(t => {
                try
                {
                    if(!wrapper.Task.IsCompleted && !t.IsFaulted && !t.IsCanceled)
                        wrapper.SetResult(port);
                    return t;
                }
                catch
                {
                    return Task.FromResult(-1);
                }
            });
        }).ToList();
    
    
        return await wrapper.Task;
    }
    

    让我知道它是否有效。我试过了 HttpClient 而不是 FtpClient .