Используете ли вы TPL для создания коллекции сокетов, излишне или есть лучший способ создать несколько сокетов? - PullRequest
1 голос
/ 06 декабря 2011

Я нахожусь в процессе обновления приложения, которое переходит от поддержки одного соединения к возможности поддерживать множество соединений. Я подумал, что использование Task Parallel Library может быть лучшим способом решения этой задачи с точки зрения создания и управления несколькими сокетами. Таким образом, я начал писать код и столкнулся с возможной проблемой при отладке. Кажется, что некоторые из моих задач, которые я создаю для настройки соединений, никогда не выполняются, и мне интересно, правильно ли я использую TPL или это что-то другое. Если я отлаживаю код, создается впечатление, что только одна задача в коллекции содержит Socket. Любая помощь приветствуется. Вот код из консольного приложения песочницы.

Основной:

        static void Main(string[] args)
        {
            Console.WriteLine("Creating connections.....");
            var sockets = CreateListeners(5);

            Console.WriteLine("Socket Info:");

            foreach (var socket in sockets)
            {                
                if (socket.Result != null)
                {
                    var con = socket.Result;
                    IPEndPoint ipep = (IPEndPoint)con.LocalEndPoint;
                    string port = ipep.Port.ToString();
                    Console.WriteLine("Socket #{0} - Listening on Port {1}:", socket.Id.ToString(), port);
                }
            }
            Console.WriteLine("Press any key to exit..");
            Console.ReadLine();
        }

Функция, которая создает список задач:

private static List<Task<Socket>> CreateListeners(int numberToCreate)
            {
                var sockets = new List<Task<Socket>>();
                for (int n = 0; n < numberToCreate; n++)
                {
                    var currentSocket = Task<Socket>.Factory.StartNew(() => CreateSocketConnection(n));
                    sockets.Add(currentSocket);
                }

                return sockets;            
            }

Функция, которая создает соединение с одним сокетом.

private static Socket CreateSocketConnection(int port)
            {

                try 
                {
                    IPAddress ipAddress = null;
                    IPHostEntry ipHostInfo = Dns.GetHostEntry(Dns.GetHostName());
                    if (Socket.OSSupportsIPv6)
                    {
                        ipAddress = ipHostInfo.AddressList[1];
                    }
                    else
                    {
                        ipAddress = ipHostInfo.AddressList[0];
                    }


                    //Create a new connection on specified port
                    IPEndPoint endPoint = new IPEndPoint(ipAddress, port+6000);
                    Socket listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                    listener.Bind(endPoint);
                    //listener.Blocking = false;
                    listener.Listen(500);

                    return listener;

                }
                catch (Exception ex)
                {
                   throw ex;        
                }
                finally
                {

                }

                return null;            
            }

Ответы [ 3 ]

2 голосов
/ 07 декабря 2011

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

После вашего звонка CreateListeners вы знаете, что запустили 5 задач,Вы позволяете библиотеке задач решить вопрос о параллелизме или последовательности.

Следовательно, вам нужно вызвать socket.Wait(); до if (socket.Result != null), потому что он может быть нулевым, если он еще не завершен.

Как заявил VirtualBlackFox, он вполне может выполнять их последовательно, если сочтет это целесообразным, что может означать, что, когда вы начинаете запрашивать результаты, только одна задача уже выполнена.

Как общий ответ, это неМне кажется неуместным использовать парализм для этой операции.Операции типа ввода-вывода, как правило, могут извлечь большую пользу из парализма, поскольку они связаны с существенными накладными расходами.

1 голос
/ 07 декабря 2011

Если вы не помечаете свои задачи как LongRunning, они используют потоки пула потоков (хорошо в текущей реализации), и TPL решает, когда действительно выполнять их: он может запускать их все последовательно один за другим в том же потоке, если он хочет, и в вашем случае это не будет хорошо

Task<Socket>.Factory.StartNew(start, CancellationToken.None, 
    TaskCreationOptions.LongRunning, // The interpretation depends on scheduler
    TaskScheduler.Default // The default one place them in their own thread
    );

Также, как сказано в моем комментарии

throw ex;

удаляет всю информацию о трассировке стека из вашего исключения, и при отладке вы никогда не захотите этого, используйте один из тезисов:

throw; // To re-throw the same exception
throw new Exception("bla", ex); // To specify a new message, or change the
                                // exception type. A new stack trace is created
                                // but you can still access the original one
                                // using InnerException
0 голосов
/ 09 декабря 2011

В этой ссылке есть хорошая статья о том, как использовать асинхронный TCP, который использует пул потоков ввода-вывода для обратных вызовов.

http://msdn.microsoft.com/en-us/library/5w7b7x5f(v=VS.100).aspx

Поскольку вы хотите привязать несколько портов, вместоWaitOne, создавая по одному для каждого сокета и используя WaitAny.Таким образом, вы все еще можете прослушивать один поток.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...