Главная Учебники - Разные Лекции (разные) - часть 33
АВТОНОМНАЯ НЕКОМЕРЧЕСКАЯ ОБРАЗОВАТЕЛЬНАЯ ОРГАНИЗАЦИЯВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯВОРОНЕЖСКИЙ ЭКОНОМИКО-ПРАВОВОЙ ИНСТИТУТ ФИЛИАЛ г.СТАРЫЙ ОСКОЛ БЕЛГОРОДСКОЙ ОБЛАСТИ ЭКОНОМИЧЧЕСКИЙ ФАКУЛЬТЕТ КАФЕДРА «ГУМАНИТАРНЫХ И ЕСТЕСТВЕННОНАУЧНЫХ ДИСЦИПЛИН» По дисциплине: Проектирование информационных систем На тему Проектирование и разработка программы ЧАТ для локальной сети Выполнил Кирилов О.И. Содержание
Введение Глава I. История возникновения чата Виды чатов Глава II. Создание многопользовательского чата Листинг программы Заключение Приложение Введение
Сокеты – это интерфейс прикладного программирования для сетевых приложений TCP/IP. Интерфейс сокетов был создан в восьмидесятых годах для операционной системы UNIX. Позднее интерфейс сокетов был перенесен в MicrosoftWindows. Сокеты до сих пор используются в приложениях для сетей TCP/IP. В переводе с английского "sockets" – гнезда, т.е. сетевые приложения используют сокеты, как виртуальные разъемы для обмена данными между собой. Сокеты бывают трех видов: клиентские, слушающие и серверные. Клиентские сокеты устанавливают связь с сервером и обмениваются с ним данными. Клиентский сокет включен в компонент TClientSocket. Слушающий сокет принимает запрос на соединение от клиентского сокета, и соединяет сервер с клиентом. Слушающий сокет содержится в компоненте TServerSocket. Серверный сокет обменивается данными с клиентом по уже установленному (слушающим сокетом) соединению. Для того чтобы клиент мог установить соединение с сервером, ему необходимо указать его адрес (IP) и номер порта, через который будет происходить обмен данными. Глава
I
. История возникновения чата
Вопросы общения интересовали людей всегда. Для того, чтобы можно было обмениваться информацией не только при личной встрече, но и на огромных расстояниях, люди изобретали всё новые и новые технические средства, организовывали почтовые системы, протягивали кабели через континенты и океаны, запускали спутники связи. С развитием информационных технологий стали возможным еще более глобальные коммуникации. Историческим «докомпьютерным» предшественником чатов, несомненно, был телефон. Ни почта, ни телеграф не позволяли общаться в реальном времени и не были доступны в домашней обстановке. Изобретение и распространение телефона по планете вызвало настоящую революцию в средствах и способах общения. Возможность поговорить с собеседником на другой стороне Земли казалась настоящим чудом. Во второй половине XX века начали бурно развиваться компьютеры. Однако долгое время они были большими и слишком дорогими, что препятствовало тому, чтобы расходовать драгоценное машинное время на забавы с обменом сообщениями вместо расчетов атомных бомб. К тому же, до конца 60-х годов они не были связаны друг с другом. Предок Интернета, сеть ARPANET, в 1969 году насчитывала только четыре связанных друг с другом научных компьютера. Чуть позже, в 1971 году, была придумана электронная почта, которая стала необычайно популярна ввиду своего удобства. Постепенно появились новые службы сообщений, такие, как списки почтовой рассылки, новостные группы и доски объявлений. Однако в то время сеть ARPANET ещё не могла легко взаимодействовать с другими сетями, построенными на других технических стандартах, что затрудняло её распространение. Но тем не менее, эта проблема вскоре была решена после перехода сетей на протокол обмена данными TCP/IP, который успешно применяется до сих пор. Именно в 1983 году термин «Интернет» закрепился за сетью ARPANET. Программы для обмена текстовыми строками, несмотря на простоту самой идеи, появились не сразу. Примерно в 1974 году для мэйнфрейма PLATO был разработана программа Talkomatic, потенциально позволявшая общаться между тысячей терминалов системы. В 1980-x появилась система Freelancing' Round table. Однако по-настоящему популярным стал разработанный в 1988 году протокол, названный Internet Relay Chat (IRC), что примерно можно перевести как ретранслируемый интернет-разговор. Где-то в это же время появилось и распространилось само понятие «чат». Общение в IRC быстро стало популярным из-за простоты процесса и дружественности среды. В 1991 году во время операции «Буря в пустыне» была организована IRC-трансляция новостей — сообщения со всего мира собирались в одном месте и в режиме реального времени передавались в IRC. Есть сведения, что подобным образом IRC использовался и во время путча в СССР, когда пользователи из Москвы моментально сообщали всему миру о происходящем на улицах. Для клиентов IRC, написано множество ботов, например, Eggdrop, автоматизирующие многие рутинные операции. Самым известным из клиентов IRC стал mIRC; благодаря простой и эффективной системе команд для него было написано множество скриптов, которые также позволяют выполнять широкий спектр действий. Боты и mIRC-боты используются для различных игр в каналах — «Мафия», «Викторина», и других. Разработчики IRC настолько хорошо продумали его архитектуру, что её с тех пор практически не требовалось изменять. Конечно, у него есть недостатки: короткие сообщения, проблема с кодировками, невозможность посмотреть историю сообщений при подключении. Однако он был и остаётся популярным средством для чата, хотя и в значительной мере потеснен со своих позиций. В частности, в 1998 году был придуман похожего назначения протокол Jabber - даже его название (англ. jabber болтовня, трёп; тарабарщина) отсылало к слову chat. Jabber содержал в себе многие технические новшества и постепенно получил широкое распространение, а также стал основой многих сервисов. Были и другие протоколы, менее известные, например, SIP. Виды чатов
Существует несколько разновидностей программной реализации чатов: HTTP или веб-чаты. Такой чат выглядит как обычная веб-страница, где можно прочесть последние несколько десятков фраз, написанные участниками чата и модераторами. Чаты, использующие технологию Adobe Flash. Вместо периодической перезагрузки страницы, между клиентом и сервером открывается сокет, что позволяет моментально отправлять или получать сообщения, расходуя меньше трафика. IRC, специализированный протокол для чатов. Программы-чаты для общения в локальных сетях (например, Vypress Chat, Intranet Chat). Часто есть возможность передачи файлов. Чаты, реализованные поверх сторонних протоколов (например чат, использующий ICQ). По применению чаты делятся на: all2all - групповая коммуникация (например, IRC, Jabber, Yahoo! Chat, AVACS Live Chat); p2p - персональные коммуникации (например, ICQ, Jabber, Skype, Yahoo! Messenger, AOLInstantMessenger, Hamachi) — личное общение. Глава
II
. Создание многопользовательского чата (
Multy
-
user
on
-
line
)
Multy-user- один сервер и множество клиентов. Сервер при этом выполняет обработку входящих сообщений, пересылает их по нужным каналам, регистрирует пользователей и показывает всем, сколько пользователей общаются в текущий момент. Начнем разработку приложения чата с новой формы. Вот, что должно быть в форме: PortEdit (Edit) HostEdit (Edit) NikEdit (Edit) TextEdit (Edit) ChatMemo (Memo) ClientBtn (Button) ServerBtn (Button) SendBtn (Button) ServerSocket (ServerSocket) ClientSocket (ClientSocket) UserListView (ListView) ImageList (ImageList) ServerTimer (Timer) Компонентыизстандартногопакета Delphi ServerSocket и ClientSocket невсегдамогутбытьотображенывпалитре Internet, иихнужнозагрузитьследующимобразом: выбратьменю: Component - Install Packages… - Add., далеенужноуказатьфайл …\bin\dclsockets70.bpl. Как правило, разработка любой программы начинается с определения задач, которые она должна выполнять, и определения уже на этом этапе нужных компонентов. Программа представляет собой чат для нескольких пользователей, каждый из которых может быть как сервером, так и клиентом, значит, кидаем в форму компоненты ServerSocket и ClientSocket. Важным параметром для всех является порт. Только при одинаковом значении свойства Port, связь между ними установится. Кинем в форму компонент Edit, чтобы оперативно изменять порт, назовем его PortEdit. Для соединения с сервером необходимо указывать IP сервера или его имя, поэтому кинем еще один Edit, назовем его HostEdit. Так же нам понадобятся еще два Edit’а для указания ника и ввода текста сообщения, назовем их NikEdit и TextEdit, соответственно. Текст принимаемых и отправляемых сообщений будет отображаться в Memo, кинем его в форму и назовем ChatMemo. Установим сразу вертикальную полосу прокрутки: ScrollBars = ssVertical, и свойство ReadOnly = True. Добавим клавиши управления Button: ServerBtn – для создания/закрытия сервера, ClientBtn – для подключения/отключения клиента к серверу, SendBtn - для отправки сообщений. Изменим Caption этих клавиш на “Создать сервер”, “Подключиться” и “Отправить”, соответственно. UserListView предназначен для вывода списка пользователей, который будет динамически обновляться при подключении или отключении пользователей. Сам компонент ListView настраивается как табличный отчет: свойство ViewStyle = vsReport (стиль таблицы), свойство ShowColumnHeaders = False (не показывать имена столбцов), свойство ReadOnly = True (только отображение), свойство SmallImages = ImageList (массив с иконками). Двойным кликом на компоненте ListView выводится редактор Editing UserListView.Columns. Добавляется один столбец (порядковый номер -0). В ImageList через Add закидываются иконки, в нашем случае две, с изображением силуэта пользователя: в белом – пометка сервера, в красном – пометка клиента. Теперь разберем принцип работы сервера
. Традиционно в ServerSocket для приема клиентских пакетов используется OnClientRead, но данный способ не очень удобен, ведь для идентификации пакета (кто прислал) потребуется повозиться со структурой “прием\ответ” и решить каким образом будет происходить синхронизация. Гораздо проще и эффективнее использовать цикл по числу пользователей, в котором ведется “прослушивание” всех каналов и обработка пакета, если он пришел на конкретный канал, закрепленный за конкретным пользователем. Процедура “прослушивания” каналов выполняется в теле таймера, интервал (Interval) работы которого можно изменять по необходимости (для чата нормально 500 мс, для игр нужно существенно меньше). Вот так выглядит общая структура процедуры опроса: procedure TForm1.Timer1Timer(Sender: TObject); begin// условие на наличие установленных каналов if ServerSocket.Socket.ActiveConnections<>0 then begin // циклпосуществующимканалам for i:=1 to ServerSocket.Socket.ActiveConnections do begin // сохранимпакет (еслиничегонеприслали, попакетпустой) text:=ServerSocket.Socket.Connections.ReceiveText(); // условие, что пакет не пуст iftext<>” thenbegin{тут обработка строки, выделение составляющих кода команд (com) и пр.} // определение команд casecomofкод: begin{процедура} end; код: begin{процедура} end; ……………………………………. end; end; end; end; // разрешение на выполнение процедур обновления ifUpdDo=Truethenbegin{процедуры} // блокируем разрешение UpdDo:=False; end; end; Если заметили, что цикл начинается с единицы, а в инициализации канала странное выражение (вместо логичного начала с нуля и инициализации), то такое решение существенным образом облегчает организацию ряда процедур. Например, в списке пользователей, сервер числится под номером “0”, а клиенты - начиная с “1”. Так же удобно совмещать количество каналов (ServerSocket.Socket.ActiveConnections) с процедурами определения активности пользователей. Последнее условие в теле таймера необходимо для задержки выполнения некоторых процедур обновления. Эти процедуры должны выполняться в самом конце “прослушивания” открытых каналов, и не всегда (если будет команда). Данный алгоритм применим практически к любого рода соединениям Клиент-сервер, в том числе и для игр. Перейдем непосредственно к приложению чата и его процедурам. Проверок на корректность ввода значений в поля не будет. Создадим новый тип, для использования массива объектов, так гораздо удобнее: Type TUserList = object Status: Byte; // 1 - сервер, 2 - клиент Rec: Boolean; // отметка записи пользователя в список Name: String; // имя (ник) Image: Byte; // индекс иконки end; Вот переменные, которые понадобятся в программе: var Form1: TForm1; i, j, com, ContList: Byte; len, pos, x: Word; text, StrUserList: String; UpdDo: Boolean; Buf: array[0..3] of Byte; UserMas: array[0..255] of TUserList; //массивобъектовUItems: TListItem; Опишемпроцедуру
OnCreate
формы: procedure TForm1.FormCreate(Sender: TObject); begin // заголовокформыCaption:='Многопользовательскийчат'; Application.Title:=Caption; // предложенноезначенияпортаPortEdit.Text:='Портсервера'; // адресприпроверкепрограммынаодномПК ("самнасебя") HostEdit.Text:='Адрессервера '; // введемникпо-умолчанию, остальныеполяпростоочистимNikEdit.Text:='Ананим'; TextEdit.Clear; ChatMemo.Lines.Clear; end; Процедура
“
прослушивания
”
открытых
каналов
сервером
, выглядиттак:procedure TForm1.ServerTimerTimer(Sender: TObject); begin // условиенаналичиеустановленныхканаловif ServerSocket.Socket.ActiveConnections<>0 then begin // циклпосуществующимканаламfor i:=1 to ServerSocket.Socket.ActiveConnections do begin // сохранимпакет (еслиничегонеприслали, попакетпустой) text:=ServerSocket.Socket.Connections.ReceiveText(); // условие, что пакет не пуст if text<>” then begin // получим код команды, длину строки com:=StrToInt(Copy(text,1,1)); len:=Length(text)-1; // определение команд case com of // код приема сообщения 0: begin // добавим в ChatMemo сообщение клиента ChatMemo.Lines.Add(Copy(text,2,len)); // разошлем сообщение пользователям (кроме того, кто прислал) for j:=0 to ServerSocket.Socket.ActiveConnections-1 do begin if (j+1)<>i then ServerSocket.Socket.Connections[j].SendText(’0′+Copy(text,2,len)); end; end; // код приема ника клиента 1: begin // запишем в массив полученный ник UserMas.Name:=Copy(text,2,len); // отметим, что пользователь записан в список UserMas.Rec:=True; // обновляем список UpdateUserList; end; end; end; end; end; // разрешение на выполнение процедур обновления if UpdDo=True then begin // обновляем массив пользователей UpdateUserMas; // обновляем список пользователей UpdateUserList; // блокируем разрешение UpdDo:=False; end; end; Перевод программы в режим сервера
осуществляется клавишей “Создать сервер” (ServerBtn). Вот так выглядит процедура на нажатие клавиши ServerBtn (OnClick): procedure TForm1.ServerBtnClick(Sender: TObject); begin if ServerBtn.Tag=0 then begin // клавишу ClientBtn и поля HostEdit, PortEdit, NikEdit заблокируем ClientBtn.Enabled:=False; HostEdit.Enabled:=False; PortEdit.Enabled:=False; NikEdit.Enabled:=False; // запишем указанный порт в ServerSocket ServerSocket.Port:=StrToInt(PortEdit.Text); // запускаем сервер ServerSocket.Active:=True; // добавим в ChatMemo сообщение с временем создания ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Сервер создан.’); // изменяем тэг ServerBtn.Tag:=1; // меняем надпись клавиши ServerBtn.Caption:=’Закрыть сервер’; // включаем таймер сервера ServerTimer.Enabled:=True; // вписываем параметры сервера UserMas[0].Status:=1; UserMas[0].Rec:=True; UserMas[0].Name:=NikEdit.Text; UserMas[0].Image:=1; // разрешаем обновление UpdDo:=True; end else begin // выключаем таймер сервера ServerTimer.Enabled:=False; // стираем параметры сервера UserMas[0].Status:=0; UserMas[0].Rec:=False; UserMas[0].Name:=’Неизвестный’; UserMas[0].Image:=0; // разрешаем обновление UpdDo:=True; // очищаем список клиентов UserListView.Items.Clear; // клавишу ClientBtn и поля HostEdit, PortEdit, NikEdit разблокируем ClientBtn.Enabled:=True; HostEdit.Enabled:=True; PortEdit.Enabled:=True; NikEdit.Enabled:=True; // закрываем сервер ServerSocket.Active:=False; // выводим сообщение в ChatMemo ChatMemo.Lines.Add(’['+TimeToStr(Time)+'] Сервер закрыт.’); // возвращаем тэгу исходное значение ServerBtn.Tag:=0; // возвращаем исходную надпись клавиши ServerBtn.Caption:=’Создать сервер’; end; end; Далее идут события, которые должны происходить при определенном состоянии ServerSocket’а. Напишем процедуру, когда клиент подсоединился
к серверу (OnClientConnect): procedure TForm1.ServerSocketClientConnect(Sender: TObject; Socket: TCustomWinSocket); begin // добавим в ChatMemo сообщение с временем подключения клиента ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Подключился клиент.’); // разрешаем обновление UpdDo:=True; end; Напишем процедуру, когда клиент отключается
(OnClientDisconnect): procedure TForm1.ServerSocketClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin // добавим в ChatMemo сообщение с временем отключения клиента ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Клиент отключился.’); // разрешаем обновление UpdDo:=True; end; Отправка сообщений
. Она осуществляется нажатием клавиши “Отправить” (SendBtn), но необходима проверка режима программы сервер или клиент. Напишем ее процедуру (OnClick): procedure TForm1.SendBtnClick(Sender: TObject); begin // проверка, в каком режиме находится программа if ServerSocket.Active=True then // отправляем сообщение с сервера всем пользователям for i:=0 to ServerSocket.Socket.ActiveConnections-1 do ServerSocket.Socket.Connections.SendText(’0['+TimeToStr(Time)+'] ‘+NikEdit.Text+’: ‘+TextEdit.Text) else // отправляем сообщение с клиента ClientSocket.Socket.SendText(’0['+TimeToStr(Time)+'] ‘+NikEdit.Text+’: ‘+TextEdit.Text); // отобразим сообщение в ChatMemo ChatMemo.Lines.Add(’['+TimeToStr(Time)+'] ‘+NikEdit.Text+’: ‘+TextEdit.Text); // очищаем TextEdit TextEdit.Clear; end; Режим клиента
. При нажатии клавиши “Подключиться” (ClientBtn), блокируется ServerBtn и активируется ClientSocket. Вот процедура ClientBtn (OnClick): procedure TForm1.ClientBtnClick(Sender: TObject); begin if ClientBtn.Tag=0 then begin // клавишу ServerBtn и поля HostEdit, PortEdit заблокируем ServerBtn.Enabled:=False; HostEdit.Enabled:=False; PortEdit.Enabled:=False; // запишем указанный порт в ClientSocket ClientSocket.Port:=StrToInt(PortEdit.Text); // запишем хост и адрес (одно значение HostEdit в оба) ClientSocket.Host:=HostEdit.Text; ClientSocket.Address:=HostEdit.Text; // запускаем клиента ClientSocket.Active:=True; // изменяем тэг ClientBtn.Tag:=1; // меняем надпись клавиши ClientBtn.Caption:='Отключиться'; end else begin // клавишу ServerBtn и поля HostEdit, PortEdit разблокируем ServerBtn.Enabled:=True; HostEdit.Enabled:=True; PortEdit.Enabled:=True; // закрываем клиента ClientSocket.Active:=False; // очищаем список клиентов UserListView.Items.Clear; // выводим сообщение в ChatMemo ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Сессия закрыта.’); // возвращаем тэгу исходное значение ClientBtn.Tag:=0; // возвращаем исходную надпись клавиши ClientBtn.Caption:=’Подключиться’; end; end; ПроцедурынаOnConnect, OnDisconnect, OnRead клиентаClientSocket. Сначаланачтениесообщенияссервера (OnRead): procedure TForm1.ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket); begin // получимтекст, кодкомманды, длинустрокиtext:=Socket.ReceiveText(); com:=StrToInt(Copy(text,1,1)); len:=Length(text)-1; // определениекоммандcase com of // добавимв ChatMemo сообщениессервера0: ChatMemo.Lines.Add(Copy(text,2,len)); // отошлемсвойникнасервер1: ClientSocket.Socket.SendText('1'+NikEdit.Text); // примемстрокуспискапользователей2: begin // очищаемсписокклиентовUserListView.Items.Clear; // добавимключконцастроки (т.к. вырезкасимволовсзадержкой) text:=text+Chr(152); // укажемначальныйсимволpos:=2; // обнулимсчетчиксимволовx:=0; // пробегаемподлинестрокиспискаfor j:=2 to len+1 do begin // записываемвсчетчиксдвигx:=x+1; // еслинайденключ (отделениениковвстроке) if Copy(text,j,1)=Chr(152) then begin // добавимв UserListView строкуUItems:=UserListView.Items.Add; UItems.Caption:=Copy(text,pos,x-1); // укажемсоответствующуюиконкупользователяif pos>2 then UItems.ImageIndex:=0 else UItems.ImageIndex:=1; // изменимтекущуюпозициювстрокеспискаpos:=j+1; // обнулимсчетчиксимволовx:=0; end; end; end; end; end; Дальше обычное добавление в ChatMemo определенного сообщения: procedure TForm1.ClientSocketConnect(Sender: TObject; Socket: TCustomWinSocket); begin // добавим в ChatMemo сообщение о соединении с сервером ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Подключение к серверу.’); end; procedure TForm1.ClientSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin // добавим в ChatMemo сообщение о потере связи ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Сервер не найден.’); end; Хранителем информации о пользователях у нас выступает массив, процедура его заполнения и обновления выглядит так: procedure TForm1.UpdateUserMas; begin // очищаем массив с информацией for i:=1 to 255 do begin UserMas.Status:=0; UserMas.Rec:=False; UserMas.Name:=’Неизвестный’; UserMas.Image:=0; end; // заполняем данные пользователей if ServerSocket.Socket.ActiveConnections<>0 then begin for i:=1 to ServerSocket.Socket.ActiveConnections do begin UserMas.Status:=2; UserMas.Name:=’Неизвестный’; UserMas.Image:=0; // запрашиваем имя (ник) пользователя по его каналу (код команды - 1) ServerSocket.Socket.Connections.SendText(’1′); end; end; end; Список UserListView обновляется в следующей процедуре: procedure TForm1.UpdateUserList; begin // очищаем список клиентов UserListView.Items.Clear; // очищаем переменную StrUserList:=''; // обнуляем пометку записи ContList:=0; // пробегаем по диапазону каналов for i:=0 to 255 do begin // если запись не пустая if UserMas.Status<>0 then begin // добавим в UserListView строку UItems:=UserListView.Items.Add; UItems.Caption:=UserMas.Name; UItems.ImageIndex:=UserMas.Image; // если пользователь не записан if UserMas.Rec=False then ContList:=1; // составляем строку пользователей StrUserList:=StrUserList+UserMas.Name+Chr(152); end; end; // если все пользователи отметились, и есть хоть один канал if (ContList=0) and (ServerSocket.Socket.ActiveConnections<>0) then begin // пробегаем по всем открытым каналам for i:=0 to ServerSocket.Socket.ActiveConnections-1 do begin // отправим строку списка пользователей (код команды - 2) ServerSocket.Socket.Connections.SendText(’2′+StrUserList); end; end; end; Листинг
программы
unit MainUnit; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ScktComp, ExtCtrls, ImgList, ComCtrls, jpeg; type TForm1 = class(TForm) ServerSocket: TServerSocket; ClientSocket: TClientSocket; PortEdit: TEdit; NikEdit: TEdit; TextEdit: TEdit; ChatMemo: TMemo; HostEdit: TEdit; ServerBtn: TButton; ClientBtn: TButton; SendBtn: TButton; Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; Label5: TLabel; UserListView: TListView; Label6: TLabel; ImageList: TImageList; ServerTimer: TTimer; Image1: TImage; procedure FormCreate(Sender: TObject); procedure UpdateUserList; procedure UpdateUserMas; procedure ServerBtnClick(Sender: TObject); procedure ClientBtnClick(Sender: TObject); procedure ServerSocketClientConnect(Sender: TObject; Socket: TCustomWinSocket); procedure ServerSocketClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); procedure SendBtnClick(Sender: TObject); procedure ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket); procedure ClientSocketConnect(Sender: TObject; Socket: TCustomWinSocket); procedure ServerTimerTimer(Sender: TObject); procedure TextEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); private { Private declarations } public { Public declarations } end; Type TUserList = object Status: Byte; Rec: Boolean; Name: String; Image: Byte; end; var Form1: TForm1; i, j, com, ContList: Byte; len, pos, x: Word; text, StrUserList: String; UpdDo: Boolean; Buf: array[0..3] of Byte; UserMas: array[0..255] of TUserList; UItems: TListItem; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin Caption:='Многопользовательскийчат'; Application.Title:=Caption; PortEdit.Text:='Портсервера'; HostEdit.Text:='Адрессервера'; NikEdit.Text:='Ананим'; TextEdit.Clear; ChatMemo.Lines.Clear; end; procedure TForm1.UpdateUserList; begin UserListView.Items.Clear; StrUserList:=''; ContList:=0; For i:=0 to 255 do Begin If UserMas[i].Status<>0 then Begin UItems:=UserListView.Items.Add; UItems.Caption:=UserMas[i].Name; UItems.ImageIndex:=UserMas[i].Image; If UserMas[i].Rec=False then ContList:=1; StrUserList:=StrUserList+UserMas[i].Name+Chr(152); end; end; If (ContList=0) And (ServerSocket.Socket.ActiveConnections<>0) then Begin For i:=0 to ServerSocket.Socket.ActiveConnections-1 do Begin ServerSocket.Socket.Connections[i].SendText('2'+StrUserList); end; end; end; procedure TForm1.UpdateUserMas; begin For i:=1 to 255 do Begin UserMas[i].Status:=0; UserMas[i].Rec:=False; UserMas[i].Name:='Неизвестный'; UserMas[i].Image:=0; end; If ServerSocket.Socket.ActiveConnections<>0 then Begin For i:=1 to ServerSocket.Socket.ActiveConnections do Begin UserMas[i].Status:=2; UserMas[i].Name:='Неизвестный'; UserMas[i].Image:=0; ServerSocket.Socket.Connections[i-1].SendText('1'); end; end; end; procedure TForm1.ServerBtnClick(Sender: TObject); begin If ServerBtn.Tag=0 then Begin ClientBtn.Enabled:=False; HostEdit.Enabled:=False; PortEdit.Enabled:=False; NikEdit.Enabled:=False; ServerSocket.Port:=StrToInt(PortEdit.Text); ServerSocket.Active:=True; ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Серверсоздан.'); ServerBtn.Tag:=1; ServerBtn.Caption:='Закрытьсервер'; ServerTimer.Enabled:=True; UserMas[0].Status:=1; UserMas[0].Rec:=True; UserMas[0].Name:=NikEdit.Text; UserMas[0].Image:=1; UpdDo:=True; end else Begin ServerTimer.Enabled:=False; UserMas[0].Status:=0; UserMas[0].Rec:=False; UserMas[0].Name:='Неизвестный'; UserMas[0].Image:=0; UpdDo:=True; UserListView.Items.Clear; ClientBtn.Enabled:=True; HostEdit.Enabled:=True; PortEdit.Enabled:=True; NikEdit.Enabled:=True; ServerSocket.Active:=False; ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Серверзакрыт.'); ServerBtn.Tag:=0; ServerBtn.Caption:='Создатьсервер'; end; end; procedure TForm1.ClientBtnClick(Sender: TObject); begin If ClientBtn.Tag=0 then Begin ServerBtn.Enabled:=False; HostEdit.Enabled:=False; PortEdit.Enabled:=False; ClientSocket.Port:=StrToInt(PortEdit.Text); ClientSocket.Host:=HostEdit.Text; ClientSocket.Address:=HostEdit.Text; ClientSocket.Active:=True; ClientBtn.Tag:=1; ClientBtn.Caption:='Отключиться'; end else Begin ServerBtn.Enabled:=True; HostEdit.Enabled:=True; PortEdit.Enabled:=True; ClientSocket.Active:=False; UserListView.Items.Clear; ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Сессиязакрыта.'); ClientBtn.Tag:=0; ClientBtn.Caption:='Подключиться'; end; end; procedure TForm1.ServerSocketClientConnect(Sender: TObject; Socket: TCustomWinSocket); begin ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Подключилсяклиент.'); UpdDo:=True; end; procedure TForm1.ServerSocketClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Клиентотключился.'); UpdDo:=True; end; procedure TForm1.SendBtnClick(Sender: TObject); begin If ServerSocket.Active=True then For i:=0 to ServerSocket.Socket.ActiveConnections-1 do ServerSocket.Socket.Connections[i].SendText('0['+TimeToStr(Time)+'] '+NikEdit.Text+': '+TextEdit.Text) else ClientSocket.Socket.SendText('0['+TimeToStr(Time)+'] '+NikEdit.Text+': '+TextEdit.Text); ChatMemo.Lines.Add('['+TimeToStr(Time)+'] '+NikEdit.Text+': '+TextEdit.Text); TextEdit.Clear; end; procedure TForm1.ClientSocketRead(Sender: TObject; Socket: TCustomWinSocket); begin text:=Socket.ReceiveText(); com:=StrToInt(Copy(text,1,1)); len:=Length(text)-1; Case com of 0: ChatMemo.Lines.Add(Copy(text,2,len)); 1: ClientSocket.Socket.SendText('1'+NikEdit.Text); 2: Begin UserListView.Items.Clear; text:=text+Chr(152); pos:=2; x:=0; For j:=2 to len+1 do Begin x:=x+1; If Copy(text,j,1)=Chr(152) then Begin UItems:=UserListView.Items.Add; UItems.Caption:=Copy(text,pos,x-1); If pos>2 then UItems.ImageIndex:=0 else UItems.ImageIndex:=1; pos:=j+1; x:=0; end; end; end; end; end; procedure TForm1.ClientSocketConnect(Sender: TObject; Socket: TCustomWinSocket); begin ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Подключениексерверу.'); end; procedure TForm1.ClientSocketDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin ChatMemo.Lines.Add('['+TimeToStr(Time)+'] Серверненайден.'); end; procedure TForm1.ServerTimerTimer(Sender: TObject); begin If ServerSocket.Socket.ActiveConnections<>0 then Begin For i:=1 to ServerSocket.Socket.ActiveConnections do Begin text:=ServerSocket.Socket.Connections[i-1].ReceiveText(); If text<>'' then Begin com:=StrToInt(Copy(text,1,1)); len:=Length(text)-1; Case com of 0: Begin ChatMemo.Lines.Add(Copy(text,2,len)); For j:=0 to ServerSocket.Socket.ActiveConnections-1 do Begin If (j+1)<>i then ServerSocket.Socket.Connections[j].SendText('0'+Copy(text,2,len)); end; end; 1: Begin UserMas[i].Name:=Copy(text,2,len); UserMas[i].Rec:=True; UpdateUserList; end; end; end; end; end; If UpdDo=True then Begin UpdateUserMas; UpdateUserList; UpdDo:=False; end; end; procedure TForm1.TextEditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); begin If Key=VK_RETURN then SendBtn.Click; end; end. Заключение
В моей курсовой работе я достиг, поставленных перед собою целей реализовав программный продукт онлайн общения - чат. Данный проект может быть развит в перспективе до более высокого уровня, добавив некоторые новые функциональных возможностей. В данный момент, в связи с бурным развитием WEB технологий, нет смысла создавать собственную программу чата. Так как в более удобно взять уже готовый, полностью завершенный чат. Приложение
Запущенный сервер: При подключение к серверу (у сервера): При подключении к серверу (у клиента):
|