ПредишенСледващото

В предишна статия ( "Създаване на клиент-сървър") е описано развитието на един прост разговор за двама потребители. Структурата на чата "главата до главата" е съвсем проста, защото има само един канал, от една страна, че сървърът, от друга - на клиента. Multy от потребителя структура е малко по-сложно. Има един сървър и множество клиенти. Сървърът в същото време изпълнява обработката на входящите съобщения, като ги изпраща на желаните канали, открива и показва всички потребители, много потребители да комуникират в момента. В тази статия ще се опитаме да се реализира всичко това.

Мултиплейър чат (Multy потребители он-лайн)

Нека да започнем разработването на IM приложение с готови форми от предишната статия, или с новото. Това е, което трябва да бъде под формата на:

Всички материали от Delphi ServerSocket и ClientSocket стандартен пакет не може винаги да се показват в интернет палитра. и те трябва да изтеглите следното:

изберете менюто: Компонент - инсталиране на пакети ... - Добавяне. След това трябва да се уточни ... \ бин \ dclsockets70.bpl файла.

Добавянето на нови компоненти:

Сега помислете принципа на сървъра. Традиционно, ServerSocket да приема клиентски пакети, използвани OnClientRead. но този метод не е много удобно, защото за идентификация пакет (който е изпратил) трябва да калайджия със структурата на "получаване \ отговор" и да реши как ще се прояви синхронизирането. Тя е много по-лесно и по-ефективно да се използва цикъл на броя на потребителите, която се провежда от "мониторинг" всички канали и пакети за обработка, ако се стигне до определен канал се възлага на определен потребител. Процедурата на "слушат" канали се извършва в интервал таймер тяло (интервалът) работа, която може да бъде на необходимостта да се промени (за чат обикновено 500 милисекунди, за игри се нуждаят от значително по-малко). Тук е общата структура на процедурата по проучване:

процедура TForm1.Timer1Timer (Sender: TObject);
започвам
// условие за инсталираните канали
ако ServerSocket.Socket.ActiveConnections<>0 след това
започвам
// линия чрез съществуващите канали
защото: = 1 до ServerSocket.Socket.ActiveConnections правят
започвам
// спаси пакета (ако нищо не се изпраща, празна опаковка)
текст: = ServerSocket.Socket.Connections [I-1] .ReceiveText ();
// посочи, че пакетът не е празна
ако текст<>'' След това
започвам

// определи команди
При ком на
код: започнете

приключи;
код: започнете

приключи;
...........................................
приключи;
приключи;
приключи;
приключи;
// разрешение за извършване на процедурите за ъпгрейд
ако UpdDo = True след това
започвам

// заключване на разрешението
UpdDo: = False;
приключи;
приключи;

Ако забележите, че цикълът започва с устройството и инициализация канал странен израз [I-1] (по-скоро, отколкото логично да се започне с нула и инициализация), след това решение значително улеснява организирането на редица процедури. Така например, в списъка с потребители, сървърът е регистрирана под номер "0", а клиентите - като се започне с "1". Също така е удобно да се съчетаят на броя на каналите (ServerSocket.Socket.ActiveConnections) с процедурите за определяне на активността на потребителя.
Последното условие в таймера на тялото е необходимо да се отложи прилагането на някои процедури за обновяване. Тези процедури трябва да се извършват в края на "слушане на" отворени канали, както и не винаги (ако отбор).
Този алгоритъм е приложима за почти всякакъв вид клиент-сървър, включително игри.

ВНИМАНИЕ! Ако не сте сигурни в коректността на кода, който пише за обработка на инструкции във веригата, "слушане на" отворени канали, функцията се прилага винаги Try..Except..End. което ще се избегнат сериозни грешки, защото повторяемостта на цикъл може да бъде много бързо. например:

опитвам

с изключение на
// спрете таймера и свързаните с него дейности
Край;

Отиди директно към нашия чат приложение и неговите процедури. Както и преди, на проверките за точността на стойностите за въвеждане в областта няма да бъде.
Създаване на нов тип за използване на масив от обекти, така че много по-удобно:

тип
TUserList = обект
Статус: Byte; // 1 - Сървър 2 - клиент
Rec: Булева; // маркират всички блог записи в списъка
Име: String; // име (участника)
Снимка: Byte; // индекс на икони
приключи;

Това са променливи, които ще бъдат необходими в програмата:

Var
Form1: TForm1;
И, Й, ком ContList: Byte;
лен, поз, X: Word;
текстови, StrUserList: String;
UpdDo: Булева;
Buf: масив [0..3] на байт;
UserMas: масив [0..255] на TUserList; // масив от обекти
UItems: TListItem;

Нека да описват процедурата OnCreate форми:

Процедурата на "слушане на" отворени канали сървъра изглежда така:

Преводачески софтуер в сървърен режим чрез натискане на бутона "Създаване на сървър" (ServerBtn). Това е процедурата чрез натискане на клавиш ServerBtn (OnClick):

процедура TForm1.ServerBtnClick (Sender: TObject);
започвам
ако ServerBtn.Tag = 0, тогава
започвам
// ключ 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;
край
още
започвам
// изключите таймера за сървъра
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: = "Създаване на сървър";
приключи;
приключи;

Освен това има и събития, които трябва да се появят в определено състояние ServerSocket "а. Напиши процедура, когато клиентът се свързва със сървъра (OnClientConnect):

процедура TForm1.ServerSocketClientConnect (Sender: TObject;
Гнездо: TCustomWinSocket);
започвам
// добавяне на съобщение с време ChatMemo клиент връзката
ChatMemo.Lines.Add ( '[' + TimeToStr (Time) + '] свързване на клиента.');
// позволи актуализация
UpdDo: = True;
приключи;

Напиши процедура, когато клиентът се изключва (OnClientDisconnect):

процедура TForm1.ServerSocketClientDisconnect (Sender: TObject;
Гнездо: TCustomWinSocket);
започвам
// добавяне на съобщение с времето ChatMemo клиент прекъсне
ChatMemo.Lines.Add ( '[' + TimeToStr (Time) + '] Клиент изключен.');
// позволи актуализация
UpdDo: = True;
приключи;

Изпращането на съобщения. Имаме го извършва чрез натискане на "Изпрати" (SendBtn), но е необходимо да се провери на режима за програмиране, сървъра или клиента. Ние напишат своята процедура (OnClick):

процедура TForm1.SendBtnClick (Sender: TObject);
започвам
// провери кой режим на програмата
ако ServerSocket.Active = True след това
// изпратите съобщението от сървъра за всички потребители
защото: = 0 до ServerSocket.Socket.ActiveConnections-1 направят
ServerSocket.Socket.Connections .SendText ( '0 [' + TimeToStr (Time) + ']' + NikEdit.Text + ':' + TextEdit.Text)
още
// Изпращане съобщение на клиент
ClientSocket.Socket.SendText ( '0 [' + TimeToStr (Time) + ']' + NikEdit.Text + ':' + TextEdit.Text);
// покаже съобщението в ChatMemo
ChatMemo.Lines.Add ( '[' + TimeToStr (Time) + ']' + NikEdit.Text + ':' + TextEdit.Text);
// изчисти TextEdit
TextEdit.Clear;
приключи;

Client режим. Когато натиснете бутона "Connect" (ClientBtn), блокиран ServerBtn и активиран ClientSocket. Това е процедурата ClientBtn (OnClick):

Процедури OnConnect. OnDisconnect. OnRead клиент ClientSocket. Първо четете съобщения от сървъра (OnRead):

След това всичко е просто, обичайната добавяне на ChatMemo конкретно послание:

процедура TForm1.ClientSocketConnect (Sender: TObject;
Гнездо: TCustomWinSocket);
започвам
// добавите връзка ChatMemo съобщение на сървъра
ChatMemo.Lines.Add ( '[' + TimeToStr (Time) + '] Свързване към сървъра ".);
приключи;

процедура TForm1.ClientSocketDisconnect (Sender: TObject;
Гнездо: TCustomWinSocket);
започвам
// добавите ChatMemo загубата на комуникация съобщение
ChatMemo.Lines.Add ( '[' + TimeToStr (Time) + '] Не е намерено сървър.');
приключи;

Хранилище на информация за нашите потребители масив от актове, процедурата за неговото завършване и ъпгрейд е както следва:

Процедура TForm1.UpdateUserMas;
започвам
// изчистване на масива на информация
защото: = 1-255 направи
започвам
UserMas .Status: = 0;
UserMas .Rec: = False;
UserMas .name: = "неизвестно";
UserMas .Image: = 0;
приключи;
// попълнете данните на потребителя
ако ServerSocket.Socket.ActiveConnections<>0 след това
започвам
защото: = 1 до ServerSocket.Socket.ActiveConnections правят
започвам
UserMas .Status: = 2;
UserMas .name: = "неизвестно";
UserMas .Image: = 0;
// попита за име (псевдоним) потребителят на неговия канал (команден код - 1)
ServerSocket.Socket.Connections [I-1] .SendText ( "1");
приключи;
приключи;
приключи;

списък UserListView се актуализира в следната процедура:

процедура TForm1.UpdateUserList;
започвам
// изчистите списъка на клиенти
UserListView.Items.Clear;
// изчистване на променливата
StrUserList: = "";
// нула, марка за запис
ContList: = 0;
// преминава през редица канали
защото: = 0-255 направи
започвам
// ако записът не е празна
ако UserMas .Status<>0 след това
започвам
// добавяне на ред UserListView
UItems: = UserListView.Items.Add;
UItems.Caption: = UserMas .name;
UItems.ImageIndex: = UserMas .Image;
// ако потребителят не се записва
ако UserMas .Rec = False след ContList: = 1;
// събира потребителски низ
StrUserList: = StrUserList + UserMas .name + Chr (152);
приключи;
приключи;
// ако не са отбелязани всички потребители, и има най-малко един канал
ако (ContList = 0) и (ServerSocket.Socket.ActiveConnections<>0), тогава
започвам
// тече през всички отворени канали
защото: = 0 до ServerSocket.Socket.ActiveConnections-1 направят
започвам
// изпрати на потребителя списък с низ (команден код - 2)
ServerSocket.Socket.Connections .SendText ( "2" + StrUserList);
приключи;
приключи;
приключи;

Това е всичко. Не се страхувайте да експериментирате, но не забравяйте основните правила за безопасно програмиране. На добър час!

Свързани статии

Подкрепете проекта - споделете линка, благодаря!