Использование многопоточности / параллельности для поиска рабочего порта - PullRequest
0 голосов
/ 09 октября 2018

У меня есть функция, подобная приведенной ниже, для подключения к 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 голосов
/ 09 октября 2018

Здесь не самое лучшее, но рабочее решение:

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, но возвращать правильный порт сложно, когда другие задачи генерируют исключения.

0 голосов
/ 15 октября 2018

Вот еще один способ сделать это.Это должно вернуться сразу после завершения первой действительной задачи:

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.

0 голосов
/ 09 октября 2018

Вы можете попробовать подключиться ко всем портам одновременно с несколькими потоками, однако производительность будет ухудшаться по мере запуска новых потоков.Я предлагаю вам попробовать с заданием.

Вот пример с заданием и событием.Это не совсем решение, но вы получите суть в этом.Возможно, вам придется изменить структуру кода.

    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();
...