Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Metodicheskie_ukazania_po_lab_1-2_Seti_i_TK.doc
Скачиваний:
17
Добавлен:
16.09.2019
Размер:
228.35 Кб
Скачать

Лабораторная работа № 2 разработка прикладной программы обмена сообщениями в локальной сети

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

Для разработки программы необходимо использовать среду Delphi версии 5.0 или старше под управлением ОС Windows NT/2000/XP.

Краткие теоретические сведения

Работая в сети Internet, мы очень часто встречаемся с разного рода многопользовательскими программами. Ими могут быть почтовые клиенты, программы обмена сообщениями (чаты), форумы, FTP клиенты и т.п. Все эти приложения используют для своей работы разного рода протокола: FTP, POP, SMTP, HTTP, и т.д. Но базовым для них является единый протокол - TCP/IP.

Типичное же приложение TCP/IP построено на клиент-серверной архитектуре. Примером приложения построенного на данной архитектуре, является чат реального времени. Для этой цели необходимы компоненты TClientSocket и TServerSocket. Выбранные компоненты при работе с протоколом TCP/IP используют интерфейс сокетов.

  Сокеты – это интерфейс прикладного программирования для сетевых приложений TCP/IP. Интерфейс сокетов был создан в восьмидесятых годах для операционной системы UNIX. Позднее интерфейс сокетов был перенесен в Microsoft Windows. Сокеты до сих пор используются в приложениях для сетей TCP/IP. В переводе с английского "sockets" – гнезда, т.е. сетевые приложения используют сокеты, как виртуальные разъемы для обмена данными между собой. Сокеты бывают трех видов: клиентские, слушающие и серверные.

Клиентские сокеты устанавливают связь с сервером и обмениваются с ним данными. Клиентский сокет включен в компонент TClientSocket. Слушающий сокет принимает запрос на соединение от клиентского сокета, и соединяет сервер с клиентом. Слушающий сокет содержится в компоненте TServerSocket. Серверный сокет обменивается данными с клиентом по уже установленному (слушающим сокетом) соединению.

Для того чтобы клиент мог установить соединение с сервером, ему необходимо указать его адрес (IP или Host) и номер порта, через который будет происходить обмен данными (значение по умолчанию – 1001), а также ввести свой псевдоним (Nickname).

Примечание:

По умолчанию в DELPHI7 в панели компонентов отсутствуют необходимые элементы - ServerSocket1 и ClientSocket1. Их нужно добавить путем установки пакета dclsockets70.bpl.

Для этого необходимо зайти в меню Component  Install Packages… Add  dclsockets70.bpl  и указывать путь к пакету с компонентами, Обычно находиться в корневом папке Delphi в паке Bin. (например C:\Program Files\Borland\Delphi7\Bin\ dclsockets70.bpl ).

После такого добавления во вкладке Internet должны появиться данные компоненты.

         Пример чата для локальной сети

В этом примере два приложения - чат-сервер и чат-клиент. Чат-клиенты подключаются к чат-серверу и через него обмениваются сообщениями. Чат-сервер может быть запущен и на том компьютере, где запущен один из клиентов. Кроме того, для тестирования Вы можете запустить на своем компьютере сразу чат-сервер и несколько чат-клиентов. Для этого нужно указать localhost в поле Host, а в поле Port у сервера и у клиента должны быть одинаковые значения. Не путайте сервер в понимании программы, принимающей вызовы клиентов, с компьютером-сервером! То же самое и с клиентом.

Разработка чат-сервера

1. Создайте форму и разместите на ней следующие компоненты: ListBox1, ServerSocket1 и Panel1. На к омпоненте Panel1 разместите Button1 (Start), Button2 (Stop). (рис.3)

Рис.3 Пример окна формы чат-сервера

2. Введите текст процедуры обработки события нажатия на кнопку Button1:

{Запуск сервера} procedure TForm1.Button1Click(Sender: TObject); var s: string; begin   {Запрашиваем порт}   s := InputBox('Start chat server','Enter port:','1001');   if s = '' then     Exit;   {Чистим лист пользователей}   ListBox1.Items.Clear;   {Устанавливаем порт}   ServerSocket1.Port := StrToInt(s);   {Запускаем сервер}   ServerSocket1.Open; end;

3. Введите текст процедуры обработки события нажатия на кнопку Button2:

procedure TForm1.Button2Click(Sender: TObject); begin   {Чистим юзер лист и останавливаем сервер}   ListBox1.Items.Clear;   if ServerSocket1.Active then     ServerSocket1.Close; end;

4. Введите текст процедуры обработки события OnClientRead для компонента ServerSocket1:

procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); var s: string;   i: Integer; begin   {сохраняем в s присланную нам строку}   s := Socket.ReceiveText;   {Если кто-то прислал нам свое имя}   if Copy(s,1,2) = '#N' then begin     Delete(s,1,2);     {Добавляем его в юзер лист}     ListBox1.Items.Add(s);     {Записываем в s команду для посылки нового списка пользователей}     s := '#U';     for i := 0 to ListBox1.Items.Count-1 do       s := s+ListBox1.Items[i]+';';     {...и рассылаем этот список всем клиентам}     for i := 0 to ServerSocket1.Socket.ActiveConnections-1 do       ServerSocket1.Socket.Connections[i].SendText(s);     Exit;   end;   {Если кто-то отправил сообщение - рассылаем его всем клиентам}   if (Copy(s,1,2) = '#M')or(Copy(s,1,2) = '#P') then begin     for i := 0 to ServerSocket1.Socket.ActiveConnections-1 do       ServerSocket1.Socket.Connections[i].SendText(s);     Exit;   end; end;

Первая строка - сохраняет полученные из сокета данные в s. Далее, функция Copy(s,1,2) возвращает первые два символа строки s, которые затем проверяются на соответствие с условно-введенной нами командой "#N", которая означает, что в строке s после самой команды содержится присланное кем-либо из клиентов имя (ник - псевдоним). Это имя затем добавляется в ListBox1. Таким образом ListBox1 становится списком подключенных клиентов.

Далее, в строку s (информация в которой уже не нужна) записывается команду "#U" и последовательно всех пользователей из списка ListBox1. Затем всю эту строку рассылаем ВСЕМ клиентам.

Затем, если получили не "#N", а "#M" или "#P" (простое или приватное сообщение) - рассылаем его всем клиентам.

5. Введите текст процедуры обработки событий OnClientDisconnect и OnAccept для компонента ServerSocket1:

procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); var i: Integer; begin   {Кто-то присоединился или отсоединился? Запрашиваем у всех  пользователей их имена}   ListBox1.Items.Clear;   for i := 0 to ServerSocket1.Socket.ActiveConnections-1 do     ServerSocket1.Socket.Connections[i].SendText('#N'); end;

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

Примечание:

Данный пример дан в упрощенной форме. Возможности нормального чата должны быть намного шире. Также имейте в виду, что команды типа "#N", "#U", "#M", и т.д. вводятся самим разработчиком для определения того, что прислали из сокета. Эти команды никак не привязаны непосредственно к сокетам.

Разработка чат-клиента

1. Создайте форму Form1 и разместите на ней следующие компоненты: Panel1, Panel2, Panel3, Memo1, ClientSocket1. На компонентe Panel1 разместите Button1 (Send), Button2 (Connect), Button3 (Disconnect), Edit1, CheckBox1 (Private message). На Panel2 – ListBox1 и Label1 (UserList). На Panel3 - Label2 (Status) и Label3 (Idle). (рис.4)

Рис.4 – Пример окна формы чат-клиента

2. Создайте форму Form2 (Connect) и разместите на ней следующие компоненты: Label1(Enter the host name…), Label2 (Host), Label3 (Port), Label4 (Nickname), Edit1, Edit2, Edit3, Button1 (Connect), Button2 (Cancel). (Рис.5)

Рис.5 – Пример окна формы подключения к серверу

3. Далее приведем исходный текст чат-клиента с необходи-мыми пояснениями:

{Для формы TForm1} var   Form1: TForm1;   nickname: string; {Ник (псевдоним)} implementation uses conn; {Юнит с диалогом установки соединения} {$R *.DFM}

// реакция на событие onClick для Button2 procedure TForm1.Button2Click(Sender: TObject); var do_connect: Boolean;   host,port: string; begin   {Показываем окно установки соединения с сервером}   Form2 := TForm2.Create(Application);   {do_connect = True, если была нажата кнопка Connect}   do_connect := (Form2.ShowModal = mrOk);   {заполнение переменных до того, как мы уничтожим форму}   host := Form2.Edit1.Text;   port := Form2.Edit2.Text;   nickname := Form2.Edit3.Text;   {Уничтожаем форму}   Form2.Free;   {Если была нажата кнопка Cancel, то уходим отсюда}   if not do_connect then     Exit;   {Если соединение уже установлено, то обрываем его}   if ClientSocket1.Active then     ClientSocket1.Close;   {Устанавливаем свойства Host и Port}   ClientSocket1.Host := host;   ClientSocket1.Port := StrToInt(port);   {Пытаемся соединиться}   ClientSocket1.Open; end;

// реакция на событие onClick для Button3 procedure TForm1.Button3Click(Sender: TObject); begin   {Закрываем соединение (если оно установлено)}   if ClientSocket1.Active then     ClientSocket1.Close; end; // реакция на события для ClientSocket1

//OnError

procedure TForm1.ClientSocket1Error(Sender: TObject;   Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin   {Если произошла ошибка, выводим ее код в Memo1}   {Insert вставляет строку в указанную позицию (в данном случае - 0 - в начало)}   Memo1.Lines.Insert(0,'Socket error ('+IntToStr(ErrorCode)+')'); end;

//OnLookUp procedure TForm1.ClientSocket1Lookup(Sender: TObject;   Socket: TCustomWinSocket); begin   {Сообщаем о том, что идет поиск хоста}   Memo1.Lines.Insert(0,'Looking up for server...'); end;

//OnConnecting procedure TForm1.ClientSocket1Connecting(Sender: TObject;   Socket: TCustomWinSocket); begin   {соединяемся...}   Memo1.Lines.Insert(0,'connecting...'); end;

//OnConnect procedure TForm1.ClientSocket1Connect(Sender: TObject;   Socket: TCustomWinSocket); begin   {соединились!}   Memo1.Lines.Insert(0,'connected!'); end;

//OnDisconnect procedure TForm1.ClientSocket1Disconnect(Sender: TObject;   Socket: TCustomWinSocket); begin   {отсоединились :(}   Memo1.Lines.Insert(0,'disconnected'); end;

//OnRead procedure TForm1.ClientSocket1Read(Sender: TObject;   Socket: TCustomWinSocket); var s,from_,to_: string; begin   {присваиваем s полученную от сервера строку}   s := Socket.ReceiveText;   {Если сервер посылает нам User List}   if Copy(s,1,2) = '#U' then begin     Delete(s,1,2);     {Чистим ListBox1}     ListBox1.Items.Clear;     {Добавляем по одному юзеру в список. Имена юзеров разделены знаком ";"}     while Pos(';',s) > 0 do begin       ListBox1.Items.Add(Copy(s,1,Pos(';',s)-1));       Delete(s,1,Pos(';',s));     end;     Exit;   end;   {Если нам прислали общее сообщение (видимое для всех юзеров)}   if Copy(s,1,2) = '#M' then begin     Delete(s,1,2);     {Добавляем его в Memo1}     Memo1.Lines.Insert(0,Copy(s,1,Pos(';',s)-1)+'> '+     Copy(s,Pos(';',s)+1,Length(s)-Pos(';',s)));     Exit;   end;   {Если нам прислали запрос на наше имя юзера}   if Copy(s,1,2) = '#N' then begin     {Посылаем ответ}     Socket.SendText('#N'+nickname);     Exit;   end;   {Если нам прислали приватное сообщение (или не нам :) )}   if Copy(s,1,2) = '#P' then begin     Delete(s,1,2);     {Выделяем в to_ - кому оно предназначено}     to_ := Copy(s,1,Pos(';',s)-1);     Delete(s,1,Pos(';',s));     {Выделяем в from_ - кем отправлено}     from_ := Copy(s,1,Pos(';',s)-1);     Delete(s,1,Pos(';',s));     {Если оно для нас, или написано нами - добавляем в Memo1     (иногда полезно убрать этот оператор if :) )}     if (to_ = nickname)or(from_ = nickname) then       Memo1.Lines.Insert(0,from_+' (private) > '+s);       Exit;     end;   end; // реакция на событие onClick для Button1 procedure TForm1.Button1Click(Sender: TObject); var s: string; begin   {Если мы хотим послать приватное сообщение, но не выбрали адресата -   нас покарают замечанием :) и выгонят из обработчика}   if (CheckBox1.Checked)and(ListBox1.ItemIndex < 0) then begin     ShowMessage('At first you should select the user in the User List!');     Exit;   end;   {Если это приватное сообщение}   if CheckBox1.Checked then     s := '#P'+ListBox1.Items[ListBox1.ItemIndex]+';' {добавляем спец.команду и адресат}   else {А если не очень приватное?}     s := '#M'; {Просто спец.команду}   {Добавляем наше имя (от кого) и само сообщение}   s := s+nickname+';'+Edit1.Text;   {Посылаем все эти данные по сокету}   ClientSocket1.Socket.SendText(s);   {И снова ждем ввода в уже чистом TEdit-е}   Edit1.Text := '';   ActiveControl := Edit1; end;

// реакция на событие onKeyDown для Edit1 procedure TForm1.Edit1KeyDown(Sender: TObject; var Key: Word;   Shift: TShiftState); begin   {Если была нажата Enter (для тех, кто с мышами не дружит) - тоже не   отказываемся послать сообщение}   if Key = VK_RETURN then     Button1.Click; end;

Button2Click - вызывает диалог установки соединения (из второго юнита), а затем устанавливает это соединение.

ClientSocket1Read - сначала мы сохраняем полученные по сокету данные в строку s. Затем, если нам прислали список других подключенных клиентов, то мы выделяем из строки s по одному юзеру и добавляем их последовательно в ListBox1. Таким образом ListBox1 становится списком юзеров. Далее - если нам прислали команду "#M" - обычное сообщение, то мы выделяем из s отправителя и само сообщение, а затем все это в стандартной для чатов форме выводим в Memo1. Если же был получен запрос на имя пользователя (ник) - команда "#N", то посылаем серверу свой ник. А если пришло приватное сообщение ("#P"), то из строки s мы выделяем имя отправителя, адресата и само сообщение. Если сообщение адресовано нам, либо мы же его и отправили - выводим его в Memo1.

Button1Click - обработчик нажатия кнопки отправки сообщения. Если поставлен флаг CheckBox1 (т.е. сообщение - приватное), а адресат в списке пользователей не выделен - выдаем ошибку. Далее формируем в s команду для отправки сообщения: сначала тип ("#P" или "#M". Если "#P", то плюс имя адресата из списка юзеров), разделитель (";"), затем - имя отправителя, разделитель, и непосредственно сам текст сообщения. Далее идет отправка строки s по сокету, очистка поля ввода сообщения, и перевод в него фокуса (курсора).

Edit1KeyDown нужен для того, чтобы при отправке сообщения вместо нажатия кнопки Button1, просто нажимать Enter.

Задание на лабораторную работу

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

Требования к оформлению отчета

Отчет должен содержать следующую информацию:

    • Описание алгоритма взаимодействия прикладной программы с внешним узлом на основе заданного протокола.

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

Контрольные вопросы

  1. Какую архитектуру используют приложения протокола TCP/IP?

  2. Что такое сокет?

  3. С помощью каких компонентов Delphi организуется чат реального времени?

  4. Назначение слушающего сокета?

  5. Что такое чат-сервер?

  6. Какое число чат-клиентов может работать с сервером одновременно?

  7. Для чего необходима регистрация клиента на сервере?

  8. Какие параметры должны совпадать у программы-сервера и программы-клиента?

  9. Что обозначают команды "#M" или "#P"?

  10. Для чего команды управления сообщениями вводятся самим пользователем?

МЕТОДИЧЕСКИЕ УКАЗАНИЯ

к лабораторным работам по курсу

«Сети ЭВМ и телекоммуникации»

для студентов специальности 200700 «Системы автоматизированного проектирования»

очной формы обучения

Составители:

Короткевич Дмитрий Эрнстович

Короткевич Светлана Ивановна

Авторская редакция

ЛР № 066815 от 25.08.99 Подписано в печать ________

Формат 60x84/16. Бумага для множительных аппаратов.

Усл. печ. л. ___, Уч-изд. л. ___. Тираж 30 экз. «С» Зак. № __

Воронежский государственный технический университет. Московский пр.,14

43

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]