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

时间:2018-10-09 08:59:26

标签: c# multithreading parallel-processing

我具有如下功能,可连接端口不断变化的FTP

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 个答案:

答案 0 :(得分:0)

这不是最好的,但可行的解决方案:

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可能可以改善这一点,但是当其他任务抛出异常时,返回正确的端口很困难。

答案 1 :(得分:0)

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

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进行了测试。

答案 2 :(得分:-2)

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

这里是Task和Event的示例。这不是完全解决方案,但是您将获得要点。您可能必须更改代码结构。

    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);
        }
    }

如果要尝试使用线程,则用以下代码替换上面代码中的Task.Run块:

new Thread(()=>{
                        if(i == obj.w) 
                        {
                            // this raises the event and calls the ShowPort method.
                            obj.OnOpen(obj, new MyEventArgs(i));
                        }
                    })).Start();