Асинхронный сервер сокет нескольких клиентов - PullRequest
12 голосов
/ 28 апреля 2011

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

http://msdn.microsoft.com/en-us/library/fx6588te.aspx

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

Однако может ли это приложение (или даже сокеты) обрабатывать несколько одновременных запросов?

  • Что произойдет, если клиент A и B соединятся одновременно?

  • Если клиент A подключается и обработка его запроса занимает 5 секунд, если клиент B подключается через секунду, должен ли он ждать, пока клиент A завершит свою работу, прежде чем начнется его обработка?

  • Или запросы клиентов A и B будут обрабатываться одновременно?

Я провел некоторое тестирование с этим, поместив команды Thread.Sleep (n) между данными приема / отправки в коде слушателя сокета. Затем я могу отправить несколько запросов в сокет, и они, кажется, обрабатываются. Однако сокет всегда обрабатывает их в одном и том же идентификаторе потока - что заставляет меня поверить, что это на самом деле не происходит одновременно.

Особенно учитывая описание Microsoft, что это приложение просто не блокирует, ожидая новых подключений - означает ли это, что может обрабатывать одновременные подключения?

Ответы [ 2 ]

30 голосов
/ 28 апреля 2011

[Обновление 2014]: Кажется, что пример был изменен с момента публикации этого ответа, как отмечалось в этой теме . Пример MSDN теперь правильно обрабатывает несколько входящих подключений. В любом случае, общий подход, описанный здесь, является правильным и, возможно, он может дать дополнительные разъяснения.


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

Прослушивание входящих соединений

Когда вы начинаете прослушивать порт, вы создаете сокет с методом обратного вызова для входящих соединений (это ссылка на пример , который вы упомянули). Это единственное гнездо слушателя для этого номера порта:

listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);

Эта строка указывает слушателю вызывать метод AcceptCallback всякий раз, когда подключается новый клиент ( обратный вызов нового соединения ). Этот метод должен выполнять свою работу быстро, так как он блокирует другие входящие соединения.

Создание выделенных сокетов обработчиков

Именно поэтому AcceptCallback должен немедленно создать выделенный "обработчик" сокета со своим собственным фоном обратный вызов данных метод (ReadCallback):

// inside AcceptCallback, we switch to the handler socket for communication
handler.BeginReceive(state.buffer, 0, StateObject.BufferSize, 0,
    new AsyncCallback(ReadCallback), state); // fired on a background thread

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

Кроме того, перед возвратом, AcceptCallback необходимо снова позвонить listener.BeginAccept, чтобы продолжить прослушивание новых входящих соединений:

// this is the same server socket we opened previously, which will now 
// continue waiting for other client connections: it doesn't care about
// the actual data transmission between individual clients
listener.BeginAccept(new AsyncCallback(AcceptCallback), listener);

Эта часть опущена в примере MSDN, то есть она может принимать только одно соединение.

Получение данных

Как только вы получите пакет данных от вашего клиента, будет вызван метод ReadCallback. Итак, внутри этого метода обратного вызова данных вам нужно прочитать и обработать полученные данные, а затем снова вызвать тот же BeginReceive метод снова (снова с ReadCallback в качестве метода обратного вызова данных).

[Изменить]

Проблема с примером MSDN заключается в том, что он допускает подключение только одного клиента (listener.BeginAccept вызывается только один раз). Чтобы разрешить несколько одновременных подключений, необходимо создать приемный сокет, используя handler.BeginReceive, а затем вызвать listener.BeginAccept, чтобы начать прослушивание новых клиентов.

0 голосов
/ 29 апреля 2011

С каждым сокетом будет связана очередь прослушивания. Это будет иметь ожидающие / частично принятые входящие соединения. Максимальное количество ожидающих соединений может быть определено программно в API listen (), который в этом примере является ничем иным, как «listener.Listen (100)». Имея здесь значение 100, сокет «слушатель» может иметь 150 (= 2 * 100/2) ожидающих соединений в очереди прослушивания.

...