Сетевое программирование в .NET, страница 28

Для реализации новой (и все еще промежуточной) версии Chat'а надо сделать так, чтобы сервер не зависал в псевдобесконечном цикле обмена сообщениями с одним клиентом, а мог в любое время воспринять сигнал о появлении нового клиента и попытаться образовать ноаое соединение (а значит и разъем). В ситуациях, когда мы заранее не знаем количество объектов, необходимых для работы приложения, надо прежде всего вспоминать о динамических структурах данных (коллекциях). Итак, в серверное приложение хочется добавить новый объект: ArrayList sockets.

Другим важным решением, которое надо принять для реализации обмена сообщениями, является создание отдельных потоков выполнения кода, в рамках которых сервер все-таки сможет выполнять циклы общениями с клиентами. Нам нужны циклы с неизвестным числом итераций и количество циклов должно должно быть равно числу клиентов. Многопоточная модель функционирования приложений позволяет одновременно выполнять несколько циклов общения. Каждый из них выполняется в отдельном потоке и работает с одним из разъемов, хранимых в динамической коллекции ArrayList sockets.

¨  Удалите из решения два последних проекта и добавьте два новых (того же, консольного типа). Один (с именем ChatThreadServer) будет выполнять роль сервера, а второй (ChatThreadClient)—любого из клиентов.

¨  Вставьте директивы using для доступа к подпространствам: Net, Net.Sockets, Collections, Threading.

¨  В класс ChatThreadServer (измените имя класса, если оно не такое) введите две переменных (nick и sockets).

¨  Добавьте в класс метод GetNick (такой же, как и ранее), измените код метода Main, как показано ниже, и создайте два новых статических метода Proceed и Chat (их схемы также приведены ниже).

Работая с потоками, можно выбрать две стратегии: использовать всегда существующее в .NET (и ждущее, когда им воспользуются) множество потоков (Thread Pool), или использовать классы Thread и ThreadStart для создания новых потоков. Мы выбирем второй вариант. Алгоритм работы с разъемами (метод Proceed) выглядит так:

¨  Один разъем всегда работает в главном потоке (primary thread). После создания он привязывается к локальному узлу соединения (метод Bind), настраивается на прослушивание клиентов (метод Listen) и впадает бесконечный цикл обслуживания новых клиентов.

¨  Внутри цикла главный поток замораживается в выполнении метода Accept.

¨  По приходе сообщения от клиента, главный поток создает новый разъем, вставляет его (ссылку) в коллекцию sockets, создает новый поток, в котором выполняется метод Chat, работающий с очередным клиентом.

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

Для знакомства с классом Socket добавьте к существующему решению новый проект типа Visual C# Windows Application с именем TrySocket.

¨  Переименуйте файл Form1.cs в MainForm.cs. Студия предложит заменить имя класса, согласитесь с этим.

¨  Положите на форму новый для Visual Studio элемент управления типа ToolStrip. Он пришел на смену обычной панели инструментов (Toolbar), обладает значительно большей функциональностью и идеально подходит для нашего сценария. Задайте его свойство Name)=toolStrip. ToolStrip является контейнером для других элементов управления. Мы можем выбирать их из выпадающего списка.

¨  Выберите из него элемент типа ComboBox и задайте его свойства: (Name)=toolStripCombo, Size = 200; 25.

¨  Положите на форму еще один новый элемент типа WebBrowser с именем browser. Вы, заметили, что код класса MainForm рассредоточен по двум файлам: MainForm.cs и MainForm.Designer.cs?

¨  Откройте класс MainForm в редакторе кода и добавьте в конструктор код инициализации toolStripCombo.

toolStripCombo.Items.Add("http://localhost/iisstart.htm");

toolStripCombo.Items.Add("http://localhost/intro15.htm");

toolStripCombo.SelectedIndex = 0;