Каков текущий стандарт реализации клиента асинхронного сокета?

Я ищу лучшие практики, отвечающие следующим требованиям:

  • Фреймворк, который обрабатывает несколько клиентских сокетов асинхронно.
  • Каждый протокол входящих сообщений требует, чтобы каждое сообщение было форматной строкой и помечено как завершенное символом перевода строки '\ n'.
  • Я полностью контролирую клиентскую сторону, но не сервер. Сервер принимает и отправляет сообщения на основе строк с символом перевода строки, чтобы отметить завершение сообщения.
  • Я хочу иметь возможность отправлять сообщения через каждый подключенный сокет в любой момент времени (каждый сокет может получать и отправлять сообщения).
  • Входящие сообщения следует пересылать через обратный вызов.
  • Я хочу иметь возможность выбирать в своей реализации, будут ли все входящие полные сообщения от всех подключенных сокетов перенаправляться на один единственный обратный вызов или каждый клиент сокета реализует свой собственный обратный вызов.
  • Я подключаю максимум 4 клиента / сокета. Поэтому я ищу предложения, которые используют такое ограниченное количество сокетов, но способны управлять всеми ими одновременно.

Интересно, является ли структура, в которой я использую BeginReceive и EndReceive с реализованным обратным вызовом IAsyncResult, современным, учитывая, что я нацелен на .Net 4.5. Есть ли лучшее решение, например использование NetworkStream или других вариантов API? Что действительно беспокоит меня в реализации BeginReceive / EndReceive, так это то, что после EndReceive я должен снова вызвать BeginReceive и снова зарегистрировать обратный вызов. Для меня это звучит как огромные накладные расходы. Почему нельзя добавлять новые данные асинхронно в любое время, и в то же время другой контекст создает полные сообщения, которые затем маршрутизируются через вызванное событие?

Аргумент использования IAsyncResult часто приводится в том, что обработка потока выполняется, но что говорит против следующего: использование NetworkStream и простое чтение и запись в поток. Как уже упоминалось, обмениваются только строковыми сообщениями, и каждое сообщение по протоколу помечается как завершенное символом перевода строки. Отдельная задача / поток опрашивает потоковый ридер (на основе сетевого потока) через ReadLine(). Наверное, не может быть проще, не так ли?

В основном я спрашиваю, как сделать следующий код действительно асинхронным?

public class SocketClient
{
    private TcpClient client;
    private StreamReader reader;
    private StreamWriter writer;

    public event Action<string> MessageCallback;

    public SocketClient(string hostname, int port)
    {
        client = new TcpClient(hostname, port);

        try
        {
            Stream stream = client.GetStream();
            reader = new StreamReader(stream);
            writer = new StreamWriter(stream);
            writer.AutoFlush = true;

            //Start Listener on port
            StartListener();
        }
        catch (Exception e)
        {
            throw new Exception(e.ToString());
        }
    }

    public void StartListener()
    {
        Task task = Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    if (MessageCallback != null)
                    {
                        MessageCallback(reader.ReadLine());

                    }

                    //Thread.Sleep(200);
                }
            });
    }

}

person Matt    schedule 24.02.2013    source источник
comment
если клиентский и серверный код полностью находится под вашим контролем и является .net, вы можете взглянуть на SignalR для обработки ваших сообщений. Обратной стороной является то, что для WebSockets вам нужно будет использовать Windows 8 или 2012 в качестве сервера.   -  person Aron    schedule 24.02.2013
comment
Другой вариант - использовать Rx.Net вместе с Task.Factory.FromAsync для создания IObservable ‹string›, на который вы затем подписываетесь.   -  person Aron    schedule 24.02.2013
comment
@Freddy - Не могли бы вы прояснить, ищете ли вы библиотеку .net, которая имеет запрошенные функции, или как вы можете добиться необходимых функций самостоятельно, используя ТОЛЬКО фреймворк .net 4.5?   -  person MarcF    schedule 24.02.2013
comment
@MarcF, библиотеки достаточно, хотя мне интересно, что не так (или чем следующее уступает использованию BeginReceive / EndReceive): использование NetworkStream и просто чтение и запись в поток. Как уже упоминалось, обмениваются только строковыми сообщениями, и каждое сообщение по протоколу помечается как завершенное символом перевода строки.   -  person Matt    schedule 24.02.2013
comment
@Aron, извините, если я не правильно понял, я не ищу решение ASP / Web. Мне нужно обработать до 100 входящих строковых сообщений через TCP в консольном приложении C #. У меня нет контроля над сервером, и протокол исправлен.   -  person Matt    schedule 24.02.2013


Ответы (2)


На данный момент нет действующего стандарта или общепринятой практики. У вас есть несколько вариантов, каждый из которых имеет свои преимущества и недостатки:

  1. Wrap the TAP methods into Tasks and use async/await.
    • Advantages: Pretty straightforward to do.
    • Недостатки: низкоуровневый; вы должны сами управлять всеми различными async операциями в «бесконечном» цикле, а также управлять состоянием каждого соединения.
  2. Wrap the Socket *Async methods into Tasks and use async/await.
    • Advantages: Speed. This is the fastest and most scalable option.
    • Недостатки: у вас все еще есть низкоуровневые «бесконечные» циклы и управление состоянием, где код более сложен, чем вариант (1).
  3. Convert socket completions to Rx events.
    • Advantages: You can encapsulate the "infinite" loops and treat the completions as a stream of events.
    • Недостатки: для Rx требуется изрядная кривая обучения, и это непростой вариант использования. Управление состоянием каждого подключения может оказаться сложным.
  4. Convert socket completions to TPL Dataflow.
    • Advantages: (same as Rx): encapsulating the loops and getting a stream of data.
    • Недостатки: кривая обучения проще, чем Rx, но у вас все еще есть сложное управление состоянием для каждого соединения.
  5. Use an existing library such as my Nito.Async library, which provides EAP socket classes.
    • Advantages: Very easy to use; everything is an event and there's no multithreading concerns. Also, the tricker parts of state management are done for you.
    • Недостатки: хуже масштабируется, чем решения нижнего уровня.

Для вашей ситуации (несколько сотен сообщений в секунду на менее чем сотне сокетов) я бы рекомендовал использовать мою библиотеку Nito.Async. Это самый простой способ начать работать.

Что касается вашего протокола, вам придется вручную разобрать \n и выполнить собственную буферизацию. (Это верно для всех вариантов выше).

person Stephen Cleary    schedule 25.02.2013
comment
Отличный обзор и резюме. Именно то, что я искал. Большое спасибо. - person Matt; 25.02.2013
comment
еще один вопрос, где мой код вписывается в ваши разные подходы? Я пытался работать с async / await, но столкнулся с ошибками времени выполнения при отправке сообщений async, я полагаю, потому что предыдущая отправка все еще обрабатывалась, когда новая отправка попала в сокет. Где могло бы поместиться решение, которое выполняется в цикле в рамках своей собственной задачи, но отправляет / принимает синхронно? - person Matt; 26.02.2013
comment
В конце концов, я реализовал второй вариант, реализацию ожидающего сокета Стивена Туба. Спасибо, что указали мне на это. Это работает. - person Matt; 26.02.2013
comment
@Freddy Я уже сказал об этом в своем ответе I think you can jump to last paragraph's link. Для пример ожидания сокета от Стивена Туба из блога группы параллельного программирования: P - person Harsh Baid; 27.02.2013
comment
@HarshBaid, я думаю, довольно ясно, что ответ Стивена намного более исчерпывающий, и я уже указывал, что это был тот ответ, который я искал. Благодарность - person Matt; 27.02.2013
comment
@ stephen-cleary Я знаю, что это старый вопрос, но мне интересно, можете ли вы обернуть методы * Async в TaskCompletionSource вместо создания ожидаемого объекта? - person Josh; 05.12.2019
comment
@Josh: На самом деле я не слежу; чем это отличается от варианта (2) в моем ответе? - person Stephen Cleary; 05.12.2019
comment
@StephenCleary, неважно. Я кое-что перепутала. Но если у вас есть время, вы могли бы взглянуть на более конкретный вопрос, который у меня есть: stackoverflow.com/questions/59209996/. Ваш совет высоко оценен. Между прочим, я люблю твою книгу. Полезные примеры и хорошее объяснение в нескольких словах. - person Josh; 06.12.2019

По моей рекомендации используйте новую форму Async XXXReceive и 'XXXSend' (где XXX означает Begin и End), новые доступные методы: ReceiveAsync и SendAsync, которые используют SocketAsyncEventArgs, чтобы передать сокет и другую информацию о обработчиках событий обратного вызова.

Я видел хороший рабочий пример клиента и сервера сокета в архиве msdn, который масштабировался до 500 подключений (как я тестировал в одном из проектов адаптации из него), но в настоящее время я не могу найти эту ссылку из googling .. но здесь - еще одна ссылка из архива msdn по той же теме, надеюсь, она вам поможет - Станьте ближе к Связь с высокопроизводительными сокетами в .NET ..

ОБНОВИТЬ

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

Думаю, вы можете перейти по ссылке в последнем абзаце. ;)

Позвольте мне выделить один раз, потому что я хочу, я сказал SendAsync и ReceiveAsync, а не BeginReceive/EndReceive и BeginSend/EndSend, т.е. асинхронный шаблон на основе событий (EAP)

Преимущество использования асинхронной формы методов Socket заключается в том, что они представляют собой подход к программированию сокетов без исключений, который может оказаться быстрее, чем методы BeginSend / EndSend.

Вот ссылка на образец, который, как я обнаружил, полезен до 500 ПАРАЛЛЕЛЬНЫХ подключений - Примеры работы с сетью для .NET v4.0

Вам необходимо использовать функцию await / async в .NET 4.5. Вот фрагмент кода .NET 4.5, показывающий использование класса WebSocket, который также может быть адаптирован к реализациям Socket - Поддержка протокола WebSockets (я предполагаю, что AspNetWebSocketContext WebSocket будет SocketAsyncEventArgs для Socket)

Я нашел образец Awaiting Socket Operations код из MSDN - блог группы параллельного программирования, который может быть полезен для реализации await / async из. NET 4.5.

Надеюсь, это окажется для вас полезным.

person Harsh Baid    schedule 25.02.2013
comment
спасибо за ваш ответ, но ваше предложение вовсе не ново, это именно то, что я определил выше, с которого я начинаю. Я ищу альтернативы, такие как FromAsync, который в основном работает с продолжением задач, потоком данных TPL, синхронным чтением / записью сообщений на основе строк (с учетом простого протокола, который я описал выше), но в рамках своей собственной задачи с циклом while, чтобы просто назвать немного. Я видел примеры асинхронных сокетов для сервера и клиента раньше, публиковать не нужно. Как уже упоминалось, я ищу альтернативы. - person Matt; 25.02.2013
comment
честно говоря, не очень, но я ценю. Как уже указывалось, я ищу решение, отличное от типа ASP, мне нужно управлять клиентами и не могу контролировать сервер, и я не имею дело с веб-приложениями. Кроме того, я не понимаю, что вы представили, если не считать ужасного Begin / End Receive и опции AsyncCallback. - person Matt; 25.02.2013