АВТОНОМНАЯ НЕКОМЕРЧЕСКАЯ ОБРАЗОВАТЕЛЬНАЯ ОРГАНИЗАЦИЯВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯВОРОНЕЖСКИЙ ЭКОНОМИКО-ПРАВОВОЙ ИНСТИТУТ
ФИЛИАЛ г.СТАРЫЙ ОСКОЛ БЕЛГОРОДСКОЙ ОБЛАСТИ
ЭКОНОМИЧЧЕСКИЙ ФАКУЛЬТЕТ
КАФЕДРА «ГУМАНИТАРНЫХ И ЕСТЕСТВЕННОНАУЧНЫХ ДИСЦИПЛИН»
Курсовая работа
По дисциплине: Проектирование информационных систем
На тему
Проектирование и разработка программы ЧАТ для локальной сети
Выполнил
Кирилов О.И.
Содержание
Введение
Глава I. История возникновения чата
Виды чатов
Глава II. Создание многопользовательского чата
Листинг программы
Заключение
Приложение
Введение
Работая в сети Internet, мы очень часто встречаемся с разного рода многопользовательскими программами. Ими могут быть почтовые клиенты, чаты, форумы, FTP клиенты и т.п. Все эти приложения используют для своей работы разного рода протокола: FTP, POP, SMTP, HTTP, и т.д. Но базовым для них является единый протокол - TCP/IP. Типичное же приложение TCP/IP построено на клиент-серверной архитектуре. Примером приложения построенного на данной архитектуре, является чат реального времени. В данной курсовой работе я буду создавать чат средствами Borland Delphi. Для этой цели мне необходимы компоненты TClientSocket и TServerSocket Выбранные компоненты при работе с протоколом TCP/IP используют интерфейс сокетов. Но прежде чем перейти к разработке чата, разберем, что же такое сокеты.
Сокеты – это интерфейс прикладного программирования для сетевых приложений 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., далеенужноуказатьфайл …bindclsockets70.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 технологий, нет смысла создавать собственную программу чата. Так как в более удобно взять уже готовый, полностью завершенный чат.
Приложение
Запущенный сервер:
При подключение к серверу (у сервера):
При подключении к серверу (у клиента):
Отправка сообщений (у сервера и клиента):