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

История возникновения чата. Виды программной реализации чатов. Описание приложения TCP/IP, построенного на клиент-серверной архитектуре. Особенности создания многопользовательского чата (Multy-user on-line). Листинг программного продукта онлайн общения.

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

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

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

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

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

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

На тему

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

Выполнил

Кирилов О.И.


Содержание

Введение

Глава 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., далеенужноуказатьфайл …\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 технологий, нет смысла создавать собственную программу чата. Так как в более удобно взять уже готовый, полностью завершенный чат.


Приложение

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


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


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


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