Поддержка асинхронных сокетных соединений .NET в приложении Server Push - PullRequest
2 голосов
/ 18 июля 2009

Я реализовал решение, которое использует асинхронные сокеты для передачи информации с сервера на все подключенные клиенты Silverlight.

У меня возникли некоторые проблемы с Push-сервером, и мне нужно кое-что прояснить, поскольку я впервые работаю с сокетами в полностью асинхронной среде.

В данный момент Push-сервер принимает новые соединения с сокетами и добавляет их в общий список. Общий список имеет пользовательский тип, который я реализовал (названный AsyncClientConnection), используемый для управления одним асинхронным соединением сокетов.

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

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

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

Я даже спрашиваю, есть ли другой способ поддерживать типы AsyncClientConnection все вместе.

Любые предложения будут великолепны!

Большое спасибо,

-Frinny

Ответы [ 2 ]

1 голос
/ 21 июля 2009

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

В C # я использовал бы ключевое слово lock ; так как я работаю с VB.NET, я использовал ключевое слово SyncLock . Ключевое слово lock или SyncLock запрещает нескольким потокам доступ к ресурсу потока. Это работает до тех пор, пока каждый фрагмент кода блокирует ресурс перед его использованием.

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

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

Мое текущее решение состоит из 4 компонентов:

  • AsyncClientConnection: класс, который управляет логикой асинхронного подключения к сокету. Он отвечает за отправку (отправку) данных подключенному клиенту. Этот класс содержит ссылку на DataGetter.
  • PusherServer: класс, который управляет подключенными AsyncClientConnections. Он принимает входящие асинхронные соединения с сокетами и создает новые AsyncClientConnections, которые управляют этими сокетами. Также удаляются все AsyncClientConnections, которые больше не подключены.
  • DataGetter: класс, используемый для извлечения данных, которые должны быть отправлены подключенным клиентам. Полученные данные представляют собой файл изображения. DataGetter вызывает событие DataRetrieved каждый раз, когда завершает извлечение изображения.
  • TAsyncClientEventArgs: класс, который наследуется от EventArgs. Он используется для передачи данных, полученных классом DataGetter, когда он вызывает событие DataRetrieved.

PusherServer содержит экземпляр DataGetter и список AsyncClientConnections. Когда к PusherServer устанавливается новое сокетное соединение, он создает новый AsyncClientConnection и передает ему ссылку на сокетное соединение и ссылку на DataGetter.

Когда создается новый AsyncClientConnection, он указывает метод, который должен использоваться для обработки события DataRetrieved объекта DataGetter.

Теперь, когда экземпляр DataGetter вызывает событие DataRetrieved, все подключенные AsyncClientConnections 'отправляют данные, полученные клиентам. Больше нет нумерации в списке AsyncClientConnections, нет необходимости блокировать список AsyncClientConnections при отправке данных, и соединения устанавливаются быстрее и плавнее, чем в первом решении.

Надеюсь, это поможет другим, сталкивающимся с той же проблемой.

-Frinny

0 голосов
/ 21 июля 2009

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

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

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