Проектирование и разработка программы ЧАТ для локальной сети

АВТОНОМНАЯ НЕКОМЕРЧЕСКАЯ ОБРАЗОВАТЕЛЬНАЯ ОРГАНИЗАЦИЯ ВЫСШЕГО ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ ВОРОНЕЖСКИЙ ЭКОНОМИКО-ПРАВОВОЙ ИНСТИТУТ

ФИЛИАЛ г. СТАРЫЙ ОСКОЛ БЕЛГОРОДСКОЙ ОБЛАСТИ

ЭКОНОМИЧЧЕСКИЙ ФАКУЛЬТЕТ

КАФЕДРА «ГУМАНИТАРНЫХ И ЕСТЕСТВЕННОНАУЧНЫХ ДИСЦИПЛИН»















Курсовая работа

По дисциплине: Проектирование информационных систем

		На тему

		Проектирование и разработка программы ЧАТ для локальной сети





Выполнил

Кирилов О.И.


Содержание



Введение

Глава I. История возникновения чата

Виды чатов

Глава II. Создание многопользовательского чата

Листинг программы

Заключение

Приложение




Введение



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

Сокеты – это интерфейс прикладного программирования для сетевых приложений TCP/IP. Интерфейс сокетов был создан в восьмидесятых годах для операционной системы UNIX. Позднее интерфейс сокетов был перенесен в Microsoft Windows. Сокеты до сих пор используются в приложениях для сетей 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, AOL Instant Messenger, 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(); // условие, что пакет не пуст if text<>” then begin {тут обработка строки, выделение составляющих кода команд (com) и пр.} // определение команд case com of код: begin {процедура} end; код: begin {процедура} end; ……………………………………. end; end; end; end; // разрешение на выполнение процедур обновления if UpdDo=True then begin {процедуры} // блокируем разрешение 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 технологий, нет смысла создавать собственную программу чата. Так как в более удобно взять уже готовый, полностью завершенный чат.


Приложение



Запущенный сервер:



























При подключение к серверу (у сервера):



























При подключении к серверу (у клиента):



























Отправка сообщений (у сервера и клиента):