Я нахожусь в стадии разработки, когда пишу новое приложение-службу Windows, которое принимает соединения TCP / IP для длительных соединений (то есть это не то же самое, что HTTP, где много коротких соединений, а клиент подключается и остается подключенным часами или днями или ещё недели).
Я ищу идеи для лучшего способа проектирования сетевой архитектуры. Мне нужно будет запустить хотя бы один поток для сервиса. Я рассматриваю возможность использования Asynch API (BeginRecieve и т. Д.), Поскольку я не знаю, сколько клиентов я подключу в любой момент времени (возможно, сотни). Я определенно не хочу, чтобы начать поток для каждого соединения.
Данные будут в основном поступать на клиенты с моего сервера, но иногда от клиентов будут отправляться некоторые команды. Это в первую очередь приложение для мониторинга, в котором мой сервер периодически отправляет данные о состоянии клиентам.
Любые предложения о том, как сделать это максимально масштабируемым? Основной рабочий процесс? Спасибо.
РЕДАКТИРОВАТЬ: Чтобы быть ясным, я ищу решения на основе .net (C #, если это возможно, но любой язык .net будет работать)
ЗАМЕТКА: Чтобы получить награду, я ожидаю большего, чем простой ответ. Мне нужен рабочий пример решения, либо как указатель на что-то, что я могу загрузить, либо как короткий пример в строке. И это должно быть на основе .net и Windows (любой язык .net приемлем)
РЕДАКТИРОВАТЬ: Я хочу поблагодарить всех, кто дал хорошие ответы. К сожалению, я мог принять только один, и я решил принять более известный метод Begin / End. Решение Esac вполне может быть лучше, но оно все еще достаточно новое, и я не знаю точно, как оно будет работать.
Я проголосовал за все ответы, которые я считаю хорошими, я хотел бы сделать больше для вас, ребята. Еще раз спасибо.
источник
Ответы:
Я написал нечто похожее на это в прошлом. Из моего исследования, проведенного несколько лет назад, я понял, что лучше всего написать собственную реализацию сокетов, используя асинхронные сокеты. Это означало, что клиенты, которые на самом деле ничего не делали, требовали относительно небольших ресурсов. Все, что происходит, обрабатывается пулом потоков .net.
Я написал это как класс, который управляет всеми соединениями для серверов.
Я просто использовал список для хранения всех клиентских подключений, но если вам нужен более быстрый поиск для больших списков, вы можете написать его так, как хотите.
Также вам нужен сокет, который прослушивает входящие соединения.
Метод start фактически запускает сокет сервера и начинает прослушивать любые входящие соединения.
Хотелось бы отметить, что код обработки исключений выглядит плохо, но причина этого в том, что у меня был код подавления исключений, поэтому любые исключения будут подавлены и возвращены
false
если была установлена опция конфигурации, но я хотел удалить ее для ради краткости._ServerSocket.BeginAccept (новый AsyncCallback (acceptCallback)), _serverSocket), приведенный выше, по существу устанавливает сокет нашего сервера для вызова метода acceptCallback при каждом подключении пользователя. Этот метод запускается из пула потоков .Net, который автоматически обрабатывает создание дополнительных рабочих потоков, если у вас много операций блокировки. Это должно оптимально обрабатывать любую нагрузку на сервер.
Приведенный выше код, по сути, только что завершил прием входящего соединения, очереди,
BeginReceive
которая является обратным вызовом, который будет выполняться, когда клиент отправляет данные, а затем ставит в очередь следующееacceptCallback
которое примет следующее входящее соединение клиента.BeginReceive
Вызов метода является то , что говорит сокет , что делать , когда он получает данные от клиента. ДляBeginReceive
, вам нужно дать ему байтовый массив, куда он будет копировать данные, когда клиент отправляет данные.ReceiveCallback
Метод будет вызван, который , как мы обрабатываем прием данных.РЕДАКТИРОВАТЬ: В этом шаблоне я забыл упомянуть, что в этой области кода:
В общем, я бы сделал в коде все, что вы хотите, - это сделать повторную сборку пакетов в сообщения, а затем создать их как задания в пуле потоков. Таким образом, BeginReceive следующего блока от клиента не задерживается во время выполнения кода обработки сообщений.
Обратный вызов accept завершает чтение сокета данных путем вызова end receive. Это заполняет буфер, предоставленный в функции начала приема. Как только вы сделаете все, что хотите, там, где я оставил комментарий, мы вызываем следующий
BeginReceive
метод, который снова запустит обратный вызов, если клиент отправит больше данных. Теперь вот действительно сложная часть: когда клиент отправляет данные, ваш обратный вызов приема может быть вызван только с частью сообщения. Сборка может стать очень и очень сложной. Я использовал свой собственный метод и создал для этого своего рода собственный протокол. Я оставил это, но если вы попросите, я могу добавить его. Этот обработчик был на самом деле самым сложным фрагментом кода, который я когда-либо писал.Приведенный выше метод send фактически использует синхронный
Send
вызов, для меня это было хорошо из-за размеров сообщений и многопоточной природы моего приложения. Если вы хотите отправить каждому клиенту, вам просто нужно пройтись по списку _sockets.Класс xConnection, на который вы ссылаетесь выше, в основном является простой оболочкой для сокета, который включает в себя байтовый буфер, и в моей реализации некоторые дополнения.
Также для справки вот те, которые
using
я включаю, так как я всегда раздражаюсь, когда они не включены.Я надеюсь, что это полезно, это может быть не самый чистый код, но это работает. Есть также некоторые нюансы в коде, которые вы должны быть утомлены при изменении. Для одного, только один
BeginAccept
звонил в любое время. Раньше там была очень досадная ошибка .net, которая была много лет назад, поэтому я не вспоминаю подробности.Кроме того, в
ReceiveCallback
коде мы обрабатываем все, что получено из сокета, прежде чем ставить в очередь следующий прием. Это означает, что для одного сокета мы на самом деле толькоReceiveCallback
один раз в любой момент времени, и нам не нужно использовать синхронизацию потоков. Однако, если вы измените этот порядок для вызова следующего приема сразу после извлечения данных, что может быть немного быстрее, вам необходимо убедиться, что вы правильно синхронизировали потоки.Кроме того, я много взломал мой код, но оставил суть происходящего на месте. Это должно стать хорошим началом для вашего дизайна. Оставьте комментарий, если у вас есть еще вопросы по этому поводу.
источник
Send
методы будут блокироваться бесконечно в обе стороны, потому что никто не читает входные данные.Есть много способов выполнения сетевых операций в C #. Все они используют разные механизмы под капотом и, таким образом, страдают от серьезных проблем с производительностью с высоким параллелизмом. Операции Begin * являются одними из тех, которые многие ошибочно принимают за то, что они являются самым быстрым / быстрым способом создания сетей.
Для решения этих проблем они представили набор * Async методов: от MSDN http://msdn.microsoft.com/en-us/library/system.net.sockets.socketasynceventargs.aspx
Класс SocketAsyncEventArgs является частью набора улучшений класса System.Net.Sockets .. ::. Socket, которые предоставляют альтернативный асинхронный шаблон, который может использоваться специализированными высокопроизводительными приложениями сокетов. Этот класс был специально разработан для приложений сетевого сервера, которые требуют высокой производительности. Приложение может использовать расширенный асинхронный шаблон исключительно или только в целевых горячих областях (например, при получении больших объемов данных).
Главной особенностью этих улучшений является предотвращение повторного выделения и синхронизации объектов во время асинхронного ввода-вывода с большим объемом. Шаблон проектирования Begin / End, в настоящее время реализуемый классом System.Net.Sockets .. ::. Socket, требует, чтобы объект System .. ::. IAsyncResult был выделен для каждой асинхронной операции сокета.
Под прикрытием * Async API использует порты завершения ввода-вывода, которые являются самым быстрым способом выполнения сетевых операций, см. Http://msdn.microsoft.com/en-us/magazine/cc302334.aspx
И просто чтобы помочь вам, я включаю исходный код для сервера Telnet, который я написал с помощью * Async API. Я включаю только соответствующие части. Также следует отметить, что вместо обработки данных в строке я предпочитаю помещать их в очередь без блокировки (без ожидания), которая обрабатывается в отдельном потоке. Обратите внимание, что я не включаю соответствующий класс Pool, который является простым пулом, который создаст новый объект, если он пуст, и класс Buffer, который является просто саморасширяющимся буфером, который на самом деле не нужен, если вы не получаете неопределенный количество данных. Если вам нужна дополнительная информация, не стесняйтесь, присылайте мне в личку.
источник
Раньше было действительно хорошее обсуждение масштабируемого TCP / IP с использованием .NET, написанное Крисом Маллинсом из Coversant, к сожалению, похоже, что его блог исчез из прежнего местоположения, поэтому я постараюсь собрать воедино его советы из памяти (некоторые полезные комментарии его появляются в этой теме: C ++ vs. C #: разработка сервера IOCP с высокой степенью масштабируемости )
Прежде всего, обратите внимание, что и при использовании,
Begin/End
и вAsync
методахSocket
класса используются порты завершения ввода-вывода (IOCP) для обеспечения масштабируемости. Это делает гораздо большую разницу (при правильном использовании; см. Ниже) в масштабируемости, чем какой из двух методов вы фактически выбираете для реализации своего решения.Посты Криса Маллинса были основаны на использовании
Begin/End
, с которым я лично сталкивался. Обратите внимание, что Крис создал решение, основанное на этом, которое масштабировало до 10 000 одновременных клиентских подключений на 32-разрядной машине с 2 ГБ памяти и до 100 000 на 64-разрядной платформе с достаточным объемом памяти. Исходя из собственного опыта работы с этой техникой (хотя это далеко не такая нагрузка), у меня нет оснований сомневаться в этих показательных показателях.IOCP против потоковых соединений или примитивов select
Причина, по которой вы хотите использовать механизм, который использует IOCP, заключается в том, что он использует очень низкоуровневый пул потоков Windows, который не пробуждает какие-либо потоки, пока на канале ввода-вывода не появятся фактические данные, которые вы пытаетесь прочитать ( обратите внимание, что IOCP можно использовать и для файлового ввода-вывода). Преимущество этого состоит в том, что Windows не нужно переключаться на поток только для того, чтобы обнаружить, что в любом случае еще нет данных, так что это уменьшает количество переключений контекста, которые ваш сервер должен будет сделать, до минимально необходимого.
Переключение контекста - это то, что определенно убьет механизм «поток на соединение», хотя это жизнеспособное решение, если вы имеете дело только с несколькими дюжинами соединений. Этот механизм, однако, ни в коем случае не является «масштабируемым».
Важные соображения при использовании IOCP
объем памяти
Прежде всего, важно понимать, что IOCP может легко привести к проблемам с памятью в .NET, если ваша реализация слишком наивна. Каждый
BeginReceive
вызов IOCP приведет к «закреплению» буфера, в который вы читаете. Хорошее объяснение причин этой проблемы см. В блоге Юнь Джина: OutOfMemoryException и Pinning .К счастью, этой проблемы можно избежать, но она требует некоторого компромисса. Предлагаемое решение состоит в том, чтобы выделить большой
byte[]
буфер при запуске приложения (или закрыть его) размером не менее 90 КБ или около того (по состоянию на .NET 2 требуемый размер может быть больше в более поздних версиях). Причина для этого заключается в том, что большие выделения памяти автоматически оказываются в некомпактном сегменте памяти (куча больших объектов), который эффективно автоматически закрепляется. Выделив один большой буфер при запуске, вы убедитесь, что этот блок неподвижной памяти находится по относительно «низкому адресу», где он не будет мешать и вызывать фрагментацию.Затем вы можете использовать смещения для сегментирования этого большого буфера в отдельные области для каждого соединения, которое должно считывать некоторые данные. Это где компромисс вступает в игру; так как этот буфер должен быть предварительно выделен, вам нужно будет решить, сколько буферного пространства вам нужно для каждого соединения, и какой верхний предел вы хотите установить для числа соединений, которые вы хотите масштабировать (или вы можете реализовать абстракцию). который может выделить дополнительные закрепленные буферы, когда они вам понадобятся).
Простейшим решением было бы назначить каждому соединению один байт с уникальным смещением в этом буфере. Затем вы можете сделать
BeginReceive
вызов для считывания одного байта и выполнить остальное чтение в результате полученного вами обратного вызова.обработка
Когда вы получаете обратный вызов от
Begin
сделанного вами вызова, очень важно понимать, что код обратного вызова будет выполняться в низкоуровневом потоке IOCP. Абсолютно необходимо избегать длительных операций в этом обратном вызове. Использование этих потоков для сложной обработки убьет вашу масштабируемость так же эффективно, как и использование потокового соединения.Предлагаемое решение состоит в том, чтобы использовать обратный вызов только для постановки в очередь рабочего элемента для обработки входящих данных, которые будут выполнены в каком-то другом потоке. Избегайте любых потенциально блокирующих операций внутри обратного вызова, чтобы поток IOCP мог вернуться в свой пул как можно быстрее. В .NET 4.0 я бы предложил самое простое решение - создать a
Task
, указав ему ссылку на сокет клиента и копию первого байта, который уже был прочитанBeginReceive
вызовом. Затем эта задача отвечает за чтение всех данных из сокета, представляющих обрабатываемый вами запрос, его выполнение, а затемBeginReceive
повторный вызов, чтобы снова поставить в очередь сокет для IOCP. До .NET 4.0 вы можете использовать ThreadPool или создать собственную реализацию многопоточной очереди.Резюме
По сути, я бы предложил использовать пример кода Кевина для этого решения со следующими добавленными предупреждениями:
BeginReceive
уже «закреплен»BeginReceive
не делает ничего больше, чем ставит в очередь задачу для обработки фактической обработки входящих данных.Когда вы сделаете это, я не сомневаюсь, что вы сможете повторить результаты Криса, увеличив число потенциальных клиентов до сотен тысяч одновременно (при условии правильного оборудования и эффективной реализации вашего собственного кода обработки, конечно);
источник
Вы уже получили большую часть ответа через примеры кода выше. Использование асинхронной операции ввода-вывода - абсолютно верный путь. Асинхронный ввод-вывод - это способ, которым Win32 спроектирован для внутреннего масштабирования. Максимально возможная производительность, которую вы можете получить, достигается с помощью портов завершения, привязки сокетов к портам завершения и наличию пула потоков, ожидающих завершения порта завершения. Общепринятым является наличие 2-4 потоков на процессор (ядро), ожидающих завершения. Я настоятельно рекомендую ознакомиться с этими тремя статьями Рика Викика из команды Windows Performance:
Указанные статьи посвящены в основном нативному Windows API, но их обязательно нужно прочесть всем, кто пытается освоить масштабируемость и производительность. У них тоже есть некоторые заметки по управляемой стороне вещей.
Второе, что вам нужно сделать, - это прочитать книгу « Повышение производительности и масштабируемости приложений .NET» , доступную онлайн. В главе 5 вы найдете полезные и полезные советы по использованию потоков, асинхронных вызовов и блокировок. Но настоящие жемчужины есть в главе 17, где вы найдете такие полезные вещи, как практическое руководство по настройке пула потоков. У моих приложений были серьезные проблемы, пока я не настроил maxIothreads / maxWorkerThreads согласно рекомендациям в этой главе.
Вы говорите, что хотите создать чистый TCP-сервер, поэтому мой следующий пункт является ложным. Однако , если вы оказались загнаны в угол и используете класс WebRequest и его производные, имейте в виду, что за этой дверью стоит дракон: ServicePointManager . Это класс конфигурации, у которого есть одна цель в жизни: подорвать вашу производительность. Убедитесь, что вы освобождаете свой сервер от искусственно навязанного ServicePoint.ConnectionLimit, иначе ваше приложение никогда не масштабируется (я позволю вам узнать, какое значение по умолчанию ...). Вы также можете пересмотреть политику по умолчанию для отправки заголовка Expect100Continue в запросах http.
Теперь о стороне API, управляемой с помощью сокетов ядра, довольно легко на стороне отправки, но они значительно сложнее на стороне приема. Для достижения высокой пропускной способности и масштаба вы должны убедиться, что сокет не контролируется потоком, потому что у вас нет буфера, размещенного для приема. В идеале для высокой производительности вы должны отправлять вперед 3-4 буфера и размещать новые буферы, как только вы получите один обратно ( до того, как вы обработаете тот, который был возвращен), чтобы вы всегда были уверены, что в сокете всегда есть место для хранения данных, поступающих из сети. Вы поймете, почему вы, вероятно, не сможете достичь этого в ближайшее время.
Когда вы закончите играть с API BeginRead / BeginWrite и начнете серьезную работу, вы поймете, что вам нужна безопасность вашего трафика, т.е. Проверка подлинности NTLM / Kerberos и шифрование трафика или, по крайней мере, защита от вмешательства в трафик. Для этого вы используете встроенный System.Net.Security.NegotiateStream (или SslStream, если вам нужно пересечь разрозненные домены). Это означает, что вместо того, чтобы полагаться на асинхронные операции с прямым сокетом, вы будете полагаться на асинхронные операции AuthenticatedStream. Как только вы получаете сокет (либо из connect на клиенте, либо из accept на сервере), вы создаете поток в сокете и отправляете его для аутентификации, вызывая BeginAuthenticateAsClient или BeginAuthenticateAsServer. После завершения аутентификации (по крайней мере, ваш сейф от родного сумасшествия InitiateSecurityContext / AcceptSecurityContext ...) вы выполните авторизацию, проверив свойство RemoteIdentity вашего потока Authenticated и выполнив любую проверку ACL, которую должен поддерживать ваш продукт. После этого вы будете отправлять сообщения с помощью BeginWrite и получать их с помощью BeginRead. Эта проблема, о которой я говорил ранее, заключается в том, что вы не сможете разместить несколько буферов приема, потому что классы AuthenticateStream не поддерживают это. Операция BeginRead внутренне управляет всеми операциями ввода-вывода до тех пор, пока вы не получите весь кадр, в противном случае она не сможет обработать аутентификацию сообщения (расшифровать кадр и проверить подпись на кадре). Хотя по моему опыту работа, выполняемая классами AuthenticatedStream, довольно хороша и не должна иметь никаких проблем с этим. То есть. Вы должны быть в состоянии насыщать сеть ГБ только 4-5% процессором. Классы AuthenticatedStream также наложат на вас ограничения размера кадра для протокола (16k для SSL, 12k для Kerberos).
Это должно заставить вас начать на правильном пути. Я не собираюсь публиковать здесь код, есть отличный пример для MSDN . Я сделал много подобных проектов и смог масштабировать до 1000 пользователей, подключенных без проблем. Кроме того, вам нужно изменить разделы реестра, чтобы ядру было доступно больше дескрипторов сокетов. и убедитесь, что вы развертываете на серверной ОС, то есть W2K3, а не XP или Vista (т.е. клиентская ОС), это имеет большое значение.
Кстати, убедитесь, что если у вас есть операции с базами данных на сервере или файловый ввод-вывод, вы также используете асинхронную версию для них, или вы быстро истощите пул потоков. Для соединений с SQL Server убедитесь, что вы добавили «Asyncronous Processing = true» в строку соединения.
источник
У меня такой сервер работает в некоторых моих решениях. Вот очень подробное объяснение различных способов сделать это в .net: стать ближе к проводу с высокопроизводительными сокетами в .NET
В последнее время я искал способы улучшить наш код и буду искать это: « Улучшения производительности сокетов в версии 3.5 », которые были специально включены «для использования приложениями, которые используют асинхронный сетевой ввод-вывод для достижения максимальной производительности».
«Главной особенностью этих улучшений является предотвращение повторного выделения и синхронизации объектов во время асинхронного ввода-вывода с большим объемом. В настоящее время шаблон проектирования Begin / End, реализованный классом Socket для асинхронного ввода-вывода через сокет, требует наличия системы. Объект IAsyncResult должен быть выделен для каждой асинхронной операции с сокетом. "
Вы можете продолжить чтение, если перейдете по ссылке. Я лично буду тестировать их пример кода завтра, чтобы сравнить его с тем, что у меня есть.
Изменить: Здесь вы можете найти рабочий код для клиента и сервера, используя новый 3.5 SocketAsyncEventArgs, так что вы можете проверить его в течение нескольких минут и перейти к коду. Это простой подход, но он является основой для начала гораздо более широкой реализации. Кроме того, эта статья, опубликованная почти два года назад в журнале MSDN, была интересной для чтения.
источник
Рассматривали ли вы только использование TCP-привязки WCF net и шаблон публикации / подписки? WCF позволит вам сосредоточиться [в основном] на вашем домене, а не на сантехнике.
В разделе загрузок IDesign есть много примеров WCF и даже инфраструктуры публикации / подписки, которая может быть полезна: http://www.idesign.net
источник
Меня интересует одна вещь:
Это почему? Windows может обрабатывать сотни потоков в приложении, начиная хотя бы с Windows 2000. Я сделал это, с ним действительно легко работать, если потоки не нужно синхронизировать. Особенно учитывая, что вы выполняете много операций ввода-вывода (так что вы не привязаны к процессору, и многие потоки будут заблокированы на диске или в сети), я не понимаю этого ограничения.
Вы тестировали многопоточный способ и обнаружили, что в нем чего-то не хватает? Намереваетесь ли вы также иметь соединение с базой данных для каждого потока (это убьет сервер баз данных, так что это плохая идея, но ее легко решить с помощью 3-уровневого проекта). Вы беспокоитесь, что у вас будут тысячи клиентов вместо сотен, и тогда у вас действительно будут проблемы? (Хотя я бы попробовал тысячу потоков или даже десять тысяч, если бы у меня было 32+ ГБ ОЗУ - опять же, учитывая, что вы не привязаны к процессору, время переключения потоков должно быть абсолютно неважно.)
Вот код - чтобы увидеть, как это работает, перейдите на http://mdpopescu.blogspot.com/2009/05/multi-threaded-server.html и нажмите на картинку.
Класс сервера:
Основная программа сервера:
Класс клиента:
Основная программа клиента:
источник
Использование встроенного в .NET Async IO (
BeginRead
и т. Д.) Будет хорошей идеей, если вы сможете правильно понять все детали. Когда вы правильно настроите свои дескрипторы сокетов / файлов, он будет использовать базовую реализацию IOCP ОС, позволяя завершать ваши операции без использования каких-либо потоков (или, в худшем случае, с использованием потока, который, как я считаю, происходит из пула потоков ввода-вывода ядра) пула потоков .NET, который помогает уменьшить перегруженность пула потоков.)Главное, что нужно сделать, это убедиться, что вы открываете свои сокеты / файлы в неблокирующем режиме. Большинство стандартных функций удобства (например
File.OpenRead
) не делают этого, поэтому вам нужно написать свои собственные.Еще одна важная проблема - обработка ошибок - правильная обработка ошибок при написании асинхронного кода ввода-вывода намного сложнее, чем в синхронном коде. Также очень легко получить условия гонки и взаимоблокировки, даже если вы не используете потоки напрямую, поэтому вы должны знать об этом.
Если возможно, вы должны попытаться использовать вспомогательную библиотеку, чтобы облегчить процесс масштабируемого асинхронного ввода-вывода.
Microsoft Concurrency Coordination Runtime - один из примеров библиотеки .NET, созданной для облегчения трудностей, связанных с программированием такого рода. Это выглядит великолепно, но, поскольку я не использовал его, я не могу комментировать, насколько хорошо он будет масштабироваться.
Для моих личных проектов, в которых требуется асинхронный сетевой или дисковый ввод-вывод, я использую набор инструментов .NET для параллелизма / ввода-вывода, который я создал за последний год и который называется Squared.Task . Он вдохновлен библиотеками, такими как imvu.task и twisted , и я включил несколько рабочих примеров в репозиторий, которые выполняют сетевой ввод-вывод. Я также использовал его в нескольких написанных мною приложениях. Самым крупным из них является NDexer (который использует его для дискового ввода-вывода без потоков ). Библиотека была написана на основе моего опыта работы с imvu.task и имеет набор довольно комплексных модульных тестов, поэтому я настоятельно рекомендую вам попробовать ее. Если у вас есть какие-либо проблемы с этим, я был бы рад предложить вам некоторую помощь.
По моему мнению, основанный на моем опыте использования асинхронного / поточного ввода-вывода вместо потоков - это стоящее начинание на платформе .NET, если вы готовы справиться с кривой обучения. Это позволяет избежать проблем с масштабируемостью, налагаемых стоимостью объектов Thread, и во многих случаях вы можете полностью избежать использования блокировок и мьютексов, осторожно используя примитивы параллелизма, такие как Futures / Promises.
источник
Я использовал решение Кевина, но он говорит, что в решении отсутствует код для повторной сборки сообщений. Разработчики могут использовать этот код для повторной сборки сообщений:
источник
Вы можете найти хороший обзор методов на странице проблемы C10k .
источник
Вы можете попробовать использовать среду под названием ACE (Adaptive Communications Environment), которая является общей платформой C ++ для сетевых серверов. Это очень солидный, зрелый продукт, разработанный для поддержки высоконадежных приложений большого объема вплоть до телекоммуникационного класса.
Фреймворк имеет дело с довольно широким спектром моделей параллелизма и, вероятно, имеет подходящую для вашего приложения из коробки. Это должно упростить отладку системы, так как большинство неприятных проблем с параллелизмом уже решено. Компромисс здесь в том, что фреймворк написан на C ++ и не самый теплый и пушистый из кодовых баз. С другой стороны, вы получаете протестированную сетевую инфраструктуру промышленного класса и масштабируемую архитектуру из коробки.
источник
Я бы использовал SEDA или легковесную библиотеку потоков (erlang или более новые linux видят масштабируемость NTPL на стороне сервера ). Асинхронное кодирование очень громоздко, если ваше общение не так :)
источник
Ну, сокеты .NET, кажется, предоставляют select () - это лучше всего подходит для обработки ввода. Для вывода у меня будет пул потоков, записывающих сокеты, которые прослушивают рабочую очередь, принимая дескриптор / объект сокета как часть рабочего элемента, поэтому вам не нужен поток на сокет.
источник
Я бы использовал методы AcceptAsync / ConnectAsync / ReceiveAsync / SendAsync, которые были добавлены в .Net 3.5. Я сделал тест, и он примерно на 35% быстрее (время отклика и битрейт), при этом 100 пользователей постоянно отправляют и получают данные.
источник
чтобы люди скопировали и вставили принятый ответ, вы можете переписать метод acceptCallback, удалив все вызовы _serverSocket.BeginAccept (new AsyncCallback (acceptCallback), _serverSocket); и поместите его в предложение finally {} следующим образом:
Вы могли бы даже удалить первый улов, поскольку его содержимое такое же, но это метод шаблона, и вы должны использовать типизированное исключение, чтобы лучше обрабатывать исключения и понимать причину ошибки, поэтому просто реализуйте эти уловки с помощью некоторого полезного кода
источник
Я бы порекомендовал прочитать эти книги на ACE
чтобы получить представление о шаблонах, позволяющих создать эффективный сервер.
Хотя ACE реализован на C ++, книги охватывают множество полезных шаблонов, которые можно использовать на любом языке программирования.
источник
Вы не получите наивысший уровень масштабируемости, если будете использовать только .NET. GC паузы могут препятствовать задержке.
Перекрытый ввод-вывод обычно считается самым быстрым интерфейсом Windows для сетевого взаимодействия. Я не знаю, совпадает ли это с вашим Asynch API. Не используйте select, поскольку каждый вызов должен проверять каждый открытый сокет, вместо того, чтобы иметь обратные вызовы на активных сокетах.
источник
Вы можете использовать Push Framework с открытым исходным кодом для высокопроизводительной разработки серверов. Он построен на IOCP и подходит для push-сценариев и трансляции сообщений.
http://www.pushframework.com
источник