Первоначально я решил свою проблему, синхронизировав доступ к ресурсу, используемому для поддержания соединений. Я блокировал этот ресурс (общий список) всякий раз, когда добавлял к нему новое соединение. Я также блокировал ресурс всякий раз, когда просматривал его, передавая данные в подключенные сокеты.
В 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