Создание приложения Windows Forms : клиент чата на WCF



 

Для создания клиентского приложения выполним аналогичные пункту № 4 данной лабораторной работы действия.

 

Создаём новый проект в текущем решении (LWP 16). Для этого выполним следующие действия: Файл -> Создать -> Проект. В окне открывшемся окне «Создать проект», в поле Решение, выберем: Добавить в решение. В качестве проекта выберем Приложение Windows Forms. Имя будет: LWP 16- ChatClient. Жмём ОК.

 

Сразу же добавим необходимые библиотеки. Должен быть выбрать текущий проект в обозревателе решений (LWP 16- ChatClient). Выполним Проект -> Добавить ссылку... -> в окне Добавить ссылку переходим на вкладку . NET и ищем System . ServiceModel. Если ОС на которой запущена среда разработки Windows 7, добавим также ещё одну библиотеку: Microsoft Speech Object Library (вкладка COM):

 

Рис. 5. 2. Добавить ссылку: добавляем новую ссылку на библиотеку Microsoft Speech Object Library

 

Переименуем форму проекта клиентского приложений в обозревателе решений (ПКМ на иконке формы -> Переименовать). Новое имя формы будет LWP 16 MainClient. После переименования, автоматически изменится свойство ( Name ) формы.

 

Теперь изменим свойства формы LWP 16 MainClient:

 

Text изменим с Form 1 на Чат на WCF ( C #) :: Клиент

^ Поменяем заголовок формы (то, что отображается в шапке приложения слева).

MaximizeBox изменим с True на False

^ Уберём кнопку Развернуть.

Icon изменим изображение (иконку) приложения

^ Необходим файл значка *. ico.

FormBorderStyle > изменим с Sizable на FixedDialog

^ Сделаем окно «неизменяем» по размерам.

Size изменим со значений 290; 290 на 300; 125

^ Поменяем размер формы.

 

Расставим элементы с панели инструментов как показано на рисунке ниже:

 

Рис. 5. 1. Расстановка элементов на форме приложения-клиента

 

Здесь у нас в первой группе (GroupBox) «Пользовательские данные»: один Label, один TextBox и Button.

 

Свойства элементов следующие:

 

GroupBox:

( Name): GB_UserDetails
Text: Пользовательские данные

Label:

( Name): L_LoginInfo
Text: Введите имя для входа и нажмите на «Войти в чат»

TextBox:

( Name): TB_UserName

Button:

( Name): B_Login
Text: Войти в чат

 

Во второй группе элементов один GroupBox и один ListBox:

 

GroupBox:

( Name): GB_UserList
Text: Пользователи в сети

ListBox:

( Name): LB_Users

 

Последняя группа содержит один RichTextBox, TextBox и две кнопки.

 

GroupBox:

( Name): GB_MessageWindow
Text: Сообщения чата

RichTextBox:

( Name): RTB_Messages
ReadOnly: True
Multiline True

TextBox:

( Name): TB_SendMessage

Button:

( Name): B_Send
Text: Отправить

Button:

( Name): B_WakeUp
Text: !

 

Перейдём к редактированию кода формы. В начале файла с кодом для формы LWP 16 MainClient . cs объявим:

 

using System.ServiceModel;

using System.ServiceModel.Channels;

using System.Runtime.InteropServices;

using SpeechLib; // Для работы имитации голоса (в Windows XP не использовать)

 

Найдём:

 

namespace LWP16_ChatClient

{

 

Добавим после:

 

[ServiceContract(CallbackContract = typeof(ILWP16Service))]

public interface ILWP16Service // Интерфейс подключения к службе сервера

{

   [OperationContract(IsOneWay = true)] // Операция не возвращает ответовное сообщение

   void Join(string memberName); // Объявление метода присоединения к чату (по имени)

   [OperationContract(IsOneWay = true)]

   void SendMessage(string memberName, string message); // Объявление метода отсылки сообщения (по имени и тексту)

   [OperationContract(IsOneWay = true)]

   void WakeUp(string memberName, string wakeup); // Объявление метода отсылки сообщения (по имени и тексту)

   [OperationContract(IsOneWay = true)]

   void Leave(string memberName); // Объявление метода выхода из чата (по имени)

   [OperationContract(IsOneWay = true)]

   void ImageFF(string memberName, Bitmap image); // Объявление метода выхода из чата (по имени)

}

 

public interface ILWP16Channel : ILWP16Service, IClientChannel

{

}

 

Найдём:

 

public partial class LWP16MainClient : Form

{

 

Заменим на:

 

public partial class LWP16MainClient : Form, ILWP16Service

{

   [DllImport("kernel32.dll")]

   public static extern bool Beep(int BeepFreq, int BeepDuration);

 

   private delegate void UserJoined(string name);

   private delegate void UserSendMessage(string name, string message);

   private delegate void UserWakeUp(string name, string wakeup);

   private delegate void UserLeft(string name);

 

   private static event UserJoined NewJoin; // Экземпляр события присоединения к чату через делегат

   private static event UserSendMessage MessageSent; // Экземпляр события отсылки сообщения через делегат

   private static event UserWakeUp NewWakeUp; // Экземпляр события сообщения типа: "разбудить чат"

   private static event UserLeft RemoveUser; // Экземпляр события выхода из чата через делегат

 

   private string userName; // Переменная имени пользователя в чате

   private ILWP16Channel channel; // Экземпляр интерфейса чата для канала

   // class ServiceModel.DuplexChannelFactory<TChannel>

   private DuplexChannelFactory<ILWP16Channel> factory; // Объект средства приёма и передаче "дуплексных" сообщений по каналам в обе стороны

 

Метод LWP16MainClient() измени так:

 

   public LWP16MainClient()

   {

       InitializeComponent();

         this.AcceptButton = B_Login; // Привязываем событие Нажатия Enter с кнопкой "Войти в чат"

   }

 

После добавим:

 

   public LWP16MainClient(string userName)

   {

       this.userName = userName;

   }

   // Метод присоединения к чату (по имени)

   void LWP16Client_NewJoin(string name)

   {

       RTB_Messages.AppendText("\r\n");

       RTB_Messages.AppendText(name + " присоединился: [" + DateTime.Now.ToString() + "]"); // Добавляем в RichTextBox строчку c именем и датой входа пользователя          

       LB_Users.Items.Add(name); // Добавляем нового пользователя в ListBox  

   }

   // Метод отсылки сообщения (по имени и тексту)

   void LWP16Client_MessageSent(string name, string message)

   {

       if (!LB_Users.Items.Contains(name)) // Если имени нет в ListBox при получении сообщения в RichTextBox

       {

           LB_Users.Items.Add(name); // Добавляет нового пользователя в ListBox

       }

       RTB_Messages.AppendText("\r\n");

       RTB_Messages.AppendText(name + " говорит: " + message + " [" + DateTime.Now.ToString() + "]"); // Добавляем в RichTextBox строчку с именем и сообщением

       if (message == "WakeUp") { Beep(500, 100); }

   }

   // Метод присоединения к чату (по имени)

   void LWP16Client_WakeUp(string name, string wakeup)

   {

       if (!LB_Users.Items.Contains(name)) // Если имени нет в ListBox при получении сообщения в RichTextBox

       {

           LB_Users.Items.Add(name); // Добавляет нового пользователя в ListBox

       }

       RTB_Messages.AppendText("\r\n");

       RTB_Messages.AppendText(name + " попытался разбудить чат: [" + DateTime.Now.ToString() + "]"); // Добавляем в RichTextBox строчку c именем и датой входа пользователя

       if (wakeup == "WakeUp")

       {

           Beep(500, 100);

           Beep(500, 100);

           SpVoice voice = new SpVoice();

           voice.Speak("Wake Up Mate", SpeechVoiceSpeakFlags.SVSFDefault);

       }

   }

   // Метод выхода из чата (по имени)

   void LWP16Client_RemoveUser(string name)

   {

 

       try

       {

           RTB_Messages.AppendText("\r\n");

           RTB_Messages.AppendText(name + " вышел: [" + DateTime.Now.ToString() + "]"); // Добавляем в RichTextbox строчку с именем и датой выхода пользователя

           LB_Users.Items.Remove(name); // Удаляем по имени из ListBox

       }

       catch (Exception ex)

       {

           System.Diagnostics.Trace.WriteLine(ex.ToString());

       }

   }

 

   void Online(object sender, EventArgs e)

   {

       RTB_Messages.AppendText("\r\nВ сети: " + this.userName);

   }

 

   void Offline(object sender, EventArgs e)

   {

       RTB_Messages.AppendText("\r\nНе в сети: " + this.userName);

   }

 

   #region ILWP16Service Основные методы

 

   public void Join(string memberName)

      {

       if (NewJoin != null)

       {

           NewJoin(memberName);

       }

   }

 

   public void SendMessage(string memberName, string message)

   {

       if (MessageSent != null)

       {

           MessageSent(memberName, message);

       }

   }

 

   public void WakeUp(string memberName, string wakeup)

   {

       if (NewWakeUp != null)

       {

           NewWakeUp(memberName, wakeup);

       }

   }

 

   public new void Leave(string memberName)

   {

       if (RemoveUser != null)

       {

           RemoveUser(memberName);

       }

   }

 

   #endregion

 

Перейдём к событиям формы. Для кнопки «Войти в чат» событие Click будет содержать код:

 

   private void B_Login_Click(object sender, EventArgs e)

   {

       if (!string.IsNullOrEmpty(TB_UserName.Text.Trim()))

       {

 

           try

           {

               NewJoin += new UserJoined(LWP16Client_NewJoin); // Переопределяем вызов метода подключения к чату через экзмепляр события

               MessageSent += new UserSendMessage(LWP16Client_MessageSent); // Переопределяем вызов метода отсылки сообщения в чат через экзмепляр события

               NewWakeUp += new UserWakeUp(LWP16Client_WakeUp); // Переопределяем вызов метода "разбудить чат" в чат через экзмепляр события

               RemoveUser += new UserLeft(LWP16Client_RemoveUser); // Переопределяем вызов метода выхода из чата через экзмепляр события

 

               channel = null;

               this.userName = TB_UserName.Text.Trim(); // Удаляем пробелы из имени пользователя

               // class ServiceModel.InstanceContext

               InstanceContext context = new InstanceContext(new LWP16MainClient(TB_UserName.Text.Trim()));

               factory = new DuplexChannelFactory<ILWP16Channel>(context, "ChatEndPoint"); // Получаем данные из app.config и передаём данные duplex-каналу

                 channel = factory.CreateChannel(); // Создаём канал и передаём его экземпляру интерфейса чата

               // class ServiceModel.IOnlineStatus

               IOnlineStatus status = channel.GetProperty<IOnlineStatus>(); // Определяем экземпляр для индикации доступности объекта по каналу

               status.Offline += new EventHandler(Offline); // Вызов метода Offline (если в чате больше никого)

               status.Online += new EventHandler(Online); // Вызов метода Online (если в чате больше одного пользователя)              

               channel.Open(); // Открываем канал              

               channel.Join(this.userName); // Вызываем метод Join() с текущим именем пользователя введённым в TB_Username

               GB_MessageWindow.Enabled = true; // Включаем группу "Сообщения чата"

               GB_UserList.Enabled = true; // Включаем группу "Список пользователей"                  

               GB_UserDetails.Enabled = false; // Гасим группу "Данные для входа"                  

               this.AcceptButton = B_Send; // Enter = "Отослать"

               RTB_Messages.AppendText("*****************************ДОБРО ПОЖАЛОВАТЬ В ЧАТ*****************************\r\n");

               TB_SendMessage.Select();

               TB_SendMessage.Focus();

           }

           catch (Exception ex)

           {

               MessageBox.Show(ex.ToString());

           }

       }

   }

 

События Click кнопок «Отправить» и «!»:

 

   private void B_Send_Click(object sender, EventArgs e)

   {

       channel.SendMessage(this.userName, TB_SendMessage.Text.Trim()); // Вызываем метод SendMessage() и отсылаем имя пользователя и сообщение

       TB_SendMessage.Clear(); // Очищаем TB_SendMessage и далее передаём фокус на элемент и делаем его активным

       TB_SendMessage.Select();

       TB_SendMessage.Focus();

   }

 

   private void B_WakeUp_Click(object sender, EventArgs e)

   {

       channel.WakeUp(this.userName, "WakeUp"); // Вызываем метод WakeUp() и отсылаем имя пользователя и текст "WakeUp"

       TB_SendMessage.Clear(); // Очищаем TB_SendMessage и далее передаём фокус на элемент и делаем его активным

       TB_SendMessage.Select();

       TB_SendMessage.Focus();

   }

 

Событие FormClosing формы LWP16MainClient:

 

   private void LWP16MainClient_FormClosing(object sender, FormClosingEventArgs e)

   {

 

       try

       {

 

           if (channel != null)

           {

               channel.Leave(this.userName); // Если закрываем форму, вызваем метод Leave() и удаляем пользователя

               channel.Close(); // Закрываем канал между сервером и клиентом

           }

 

           if (factory != null)

           {

               factory.Close(); // Закрываем duplex-канал

           }

       }

       catch (Exception ex)

       {

           MessageBox.Show(ex.ToString());

       }

   }

 

Теперь разъясним принцип работы всего того что было тут наворочено. После запуска приложения-сервера, происходит инициализация канала по определённому адресу. Был выбран локальный адрес и порт 5433, а также абсолютное имя пространства имён LWP 16_ ChatServer для адресации по этому порту. Имя для адресации в строке конфигурации клиента:

 

<endpoint name="ChatEndPoint" address="net.p2p://LWP16-Сhat/LWP16_ChatServer"

И...

 

       <custom address="net.tcp://localhost:5433/LWP16_ChatServer"

 

Может быть любым, но! Главное чтобы оно совпадало с серверной строкой адреса (в файле конфигурации приложения-сервера). То есть, запущенное приложение-клиент должно содержать в файле конфигурации этот абсолютный адрес.

После ввода имени, становится доступной кнопка «Войти в чат». После нажатия на кнопку, в приложении-клиенте происходит вызов (через делегат и событие) метода интерфейса: Join(string memberName), содержащее переданное из текстового поля строку с именем. И далее выполняется: LWP16Client_NewJoin(string name), где в RichTextBox добавляется запись о присоединении пользователя с таким-то именем к чату и временем присоединения. Имя также добавляется в ListBox. Если пользователь в уже не один, срабатывает метод Online (), которые сразу же оповещает и пользователя и другого пользователя о том, что они «в сети». Метод срабатывает в том случае, если в сети больше одного пользователя, после нажатия кнопки «Войти в чат» пользователя ещё не вошедшего в чат. Вызывает событие после получения по каналу «индикатора события доступности»:

 

               IOnlineStatus status = channel.GetProperty<IOnlineStatus>();

 

Подключение третьего пользователя не вызывает срабатывание метода у двух предыдущих, но «в сети» отображается у нового пользователя, которому выдаётся оповещение о том что в канале он не один. Также любое подключение вызывает у всех клиентов срабатывание события NewJoin и следовательно Join (<имя подключившегося>).

Сообщение отсылается по такому же событийному механизму, но содержит также текст сообщения, которое пользователь набрал у себя в клиенте. Отправка идёт после нажатия кнопки «Отправить». В ListBox новый пользователи чата добавляются лишь по факту совершения события и появления записи с именем в RichTextBox, потом если в сети один клиент, то об имени уже находящегося в канале пользователя новый клиент узнает только после совершения тем пользователем действий.

Аналогичным образом работает событие WakeUp, которое передаёт чату имя пользователя нажавшего кнопку «!» и текст «WakeUp», который не отображается в чате. После получения такого сообщения, все клиенты издают системный звук (чтобы разбудить тех кто находится в чате). А также, если ОС на которой запущен клиент это Windows Vista или Windows 7, то приложение проговаривает заранее заготовленную фразу. Также системный звук раздаётся, если написать в чат непосредственно слово WakeUp.

Выход из чата (через закрытие формы и вызов события FormClosing) тоже выдаёт оповещения, а также происходит удаление имени вышедшего из ListBox всех клиентов.

 

Наше приложение-клиент готово. Можно компилировать и проверять работоспособность.

 

Завершающая часть

 

Компилируем приложения (Release) и запускаем. В качестве основного запускаемого объекта решения (Запускаемый проект) ставим приложение сервер. Стартуем сервер нажатием кнопки «Запуск». Запускаем копию приложения клиента. Вводим имя и жмём «Войти в чат»:

 

Рис. 6. 1. Результат работы приложения клиента после запуска сервера: вход первого пользователя в чат и отсылка сообщения

 

Запускаем второй клиент, входим в чат и пишем сообщение:

 

Рис. 6. 2. Результат работы приложения клиента после запуска сервера: вход второго пользователя в чат и его действия

 

Закрывает первый клиент, запускаем третий клиент и пишем со второго и третьего клиентов:

 

Рис. 6. 3. Результат работы приложения клиента после запуска сервера: вход третьего пользователя в чат и его действия

 


Дата добавления: 2019-09-13; просмотров: 430; Мы поможем в написании вашей работы!

Поделиться с друзьями:






Мы поможем в написании ваших работ!