Как написать масштабируемый сервер на базе Tcp / Ip - PullRequest
144 голосов
/ 15 мая 2009

Я нахожусь в стадии разработки, когда пишу новое приложение-службу Windows, которое принимает соединения TCP / IP для длительных соединений (т. Е. Это не HTTP, где много коротких соединений, а клиент подключается и остается подключенным в течение нескольких часов). или дни или даже недели).

Я ищу идеи для лучшего способа проектирования сетевой архитектуры. Мне нужно будет запустить хотя бы один поток для сервиса. Я рассматриваю возможность использования Asynch API (BeginRecieve и т. Д.), Поскольку я не знаю, сколько клиентов я подключу в любой момент времени (возможно, сотни). Я определенно не хочу запускать поток для каждого соединения.

Данные в основном будут поступать клиентам с моего сервера, но иногда от клиентов будут отправляться некоторые команды. В первую очередь это приложение для мониторинга, в котором мой сервер периодически отправляет данные о состоянии клиентам.

Есть предложения о том, как сделать это максимально масштабируемым? Основной рабочий процесс? Спасибо.

РЕДАКТИРОВАТЬ: Для ясности, я ищу решения на основе .net (если возможно, C #, но любой язык .net будет работать)

ЗАМЕТКА ПРИМЕЧАНИЕ: Чтобы получить награду, я ожидаю большего, чем простой ответ. Мне нужен рабочий пример решения, либо как указатель на что-то, что я могу загрузить, либо как короткий пример в строке. И он должен быть основан на .net и Windows (любой язык .net приемлем)

РЕДАКТИРОВАТЬ: Я хочу поблагодарить всех, кто дал хорошие ответы. К сожалению, я мог принять только один, и я решил принять более известный метод Begin / End. Решение Esac вполне может быть лучше, но оно все еще достаточно новое, и я не знаю точно, как оно будет работать.

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

Ответы [ 18 ]

1 голос
/ 22 мая 2009

Я бы использовал методы AcceptAsync / ConnectAsync / ReceiveAsync / SendAsync, которые были добавлены в .Net 3.5. Я провел эталонный тест, и он примерно на 35% быстрее (время отклика и битрейт), при этом 100 пользователей постоянно отправляют и получают данные.

1 голос
/ 15 мая 2009

Я бы использовал SEDA или облегченную библиотеку потоков (erlang или более новый linux , см. Масштабируемость NTPL на стороне сервера ). Асинхронное кодирование очень громоздко, если ваше общение не так :)

1 голос
/ 16 мая 2009

Что ж, сокеты .NET, кажется, обеспечивают select () - это лучше всего подходит для обработки ввода. Для вывода у меня будет пул потоков, записывающих сокеты, которые прослушивают рабочую очередь, принимая дескриптор / объект сокета как часть рабочего элемента, поэтому вам не нужен поток на сокет.

1 голос
/ 27 января 2015

чтобы люди копировали, вставляя принятый ответ, вы можете переписать метод acceptCallback, удалив все вызовы _serverSocket.BeginAccept (новый AsyncCallback (acceptCallback), _serverSocket); и поместите его в предложение finally {} следующим образом:

private void acceptCallback(IAsyncResult result)
    {
       xConnection conn = new xConnection();
       try
       {
         //Finish accepting the connection
         System.Net.Sockets.Socket s = (System.Net.Sockets.Socket)result.AsyncState;
         conn = new xConnection();
         conn.socket = s.EndAccept(result);
         conn.buffer = new byte[_bufferSize];
         lock (_sockets)
         {
           _sockets.Add(conn);
         }
         //Queue recieving of data from the connection
         conn.socket.BeginReceive(conn.buffer, 0, conn.buffer.Length, SocketFlags.None, new AsyncCallback(ReceiveCallback), conn);
       }
       catch (SocketException e)
       {
         if (conn.socket != null)
         {
           conn.socket.Close();
           lock (_sockets)
           {
             _sockets.Remove(conn);
           }
         }
       }
       catch (Exception e)
       {
         if (conn.socket != null)
         {
           conn.socket.Close();
           lock (_sockets)
           {
             _sockets.Remove(conn);
           }
         }
       }
       finally
       {
         //Queue the next accept, think this should be here, stop attacks based on killing the waiting listeners
         _serverSocket.BeginAccept(new AsyncCallback(acceptCallback), _serverSocket);       
       }
     }

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

1 голос
/ 15 мая 2009

Вы можете попробовать использовать среду под названием ACE (Adaptive Communications Environment), которая является общей платформой C ++ для сетевых серверов. Это очень надежный, зрелый продукт, предназначенный для поддержки высоконадежных приложений большого объема вплоть до телекоммуникационного класса.

Фреймворк имеет дело с довольно широким спектром моделей параллелизма и, вероятно, имеет одну подходящую для вашего приложения из коробки. Это должно упростить отладку системы, так как большинство неприятных проблем с параллелизмом уже решено. Компромисс здесь в том, что фреймворк написан на C ++ и не самый теплый и пушистый из кодовых баз. С другой стороны, вы получаете протестированную сетевую инфраструктуру промышленного уровня и масштабируемую архитектуру из коробки.

0 голосов
/ 16 мая 2009

Чтобы быть ясным, я ищу решения на основе .net (C #, если возможно, но любой язык .net будет работать)

Вы не получите наивысший уровень масштабируемости, если будете использовать только .NET. GC-паузы могут препятствовать задержке.

Мне нужно запустить хотя бы один поток для службы. Я рассматриваю возможность использования Asynch API (BeginRecieve и т. Д.), Поскольку я не знаю, сколько клиентов я подключу в любой момент времени (возможно, сотни). Я определенно не хочу запускать поток для каждого соединения.

Перекрытый ввод-вывод обычно считается самым быстрым API Windows для сетевого взаимодействия. Я не знаю, совпадает ли это с вашим Asynch API. Не используйте select, поскольку каждый вызов должен проверять каждый открытый сокет, вместо того, чтобы иметь обратные вызовы на активных сокетах.

0 голосов
/ 20 мая 2009

Я бы порекомендовал прочитать эти книги на ACE

чтобы получить представление о шаблонах, позволяющих создать эффективный сервер.

Хотя ACE реализован в C ++, книги охватывают множество полезных шаблонов, которые можно использовать на любом языке программирования.

0 голосов
/ 20 июля 2010

Вы можете использовать открытую среду Push Framework для высокопроизводительной разработки серверов. Он построен на IOCP и подходит для push-сценариев и трансляции сообщений.

http://www.pushframework.com

...