Для реализации новой (и все еще промежуточной) версии 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;
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.