Routing table delphi 7 клиент серверное приложение. Разработка приложения "клиент-сервер" в Delphi. Отправка массива данных

выбрать меню: 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 этих клавиш на "Создать сервер ", "Подключиться " и "Отправить ", соответственно. Последний штрих - добавим надписи Label для предания форме надлежащего вида (это по желанию).

Предупрежу сразу, проверок на корректность ввода значений в кодах не будет, поскольку это не есть главная цель. Проверки без труда вы можете прописать сами.

Определим, что должно происходить при создании формы. Опишем процедуру OnCreate :


begin
// предложенное значения порта
PortEdit.Text:="777";
// адрес при проверке программы на одном ПК ("сам на себя")
HostEdit.Text:="127.0.0.1";
// остальные поля просто очистим
NikEdit.Clear;
TextEdit.Clear;
ChatMemo.Lines.Clear;
end;

Будем считать, что выбран режим сервера. Перевод программы в режим сервера осуществляется клавишей "Создать сервер " (ServerBtn) . Чтобы не использовать лишних клавиш для отключения этого режима или компонентов типа RadioButton , можно использовать то же свойство Tag клавиши ServerBtn , изменяя его значения и выполняя те или иные операции, если значение соответствует указанному. Вот так выглядит процедура на нажатие клавиши ServerBtn (OnClick ):

procedure TForm1.ServerBtnClick(Sender: TObject);
begin
If ServerBtn.Tag=0 then
Begin
// клавишу ClientBtn и поля HostEdit, PortEdit заблокируем
ClientBtn.Enabled:=False;
HostEdit.Enabled:=False;
PortEdit.Enabled:=False;
// запишем указанный порт в ServerSocket
ServerSocket.Port:=StrToInt(PortEdit.Text);
// запускаем сервер
ServerSocket.Active:=True;
// добавим в ChatMemo сообщение с временем создания
ChatMemo.Lines.Add("["+TimeToStr(Time)+"] Сервер создан");
// изменяем тэг
ServerBtn.Tag:=1;
// меняем надпись клавиши
ServerBtn.Caption:="Закрыть сервер";
end
else
Begin
// клавишу ClientBtn и поля HostEdit, PortEdit разблокируем
ClientBtn.Enabled:=True;
HostEdit.Enabled:=True;
PortEdit.Enabled:=True;
// закрываем сервер
ServerSocket.Active:=False;

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)+"] Подключился клиент.");
end;

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

procedure TForm1.ServerSocketClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
// добавим в ChatMemo сообщение с временем отключения клиента
ChatMemo.Lines.Add("["+TimeToStr(Time)+"] Клиент отключился.");
end;

Когда на сервер приходит очередное сообщение клиента, мы должны сразу же отображать его. Напишем процедуру на чтение сообщения от клиента (OnClientRead ):


Socket: TCustomWinSocket);
begin


end;

Самое главное - отправка сообщений. У нас она осуществляется нажатием клавиши "Отправить" (SendBtn ), но необходима проверка режима программы сервер или клиент. Напишем ее процедуру (OnClick ):

procedure TForm1.SendBtnClick(Sender: TObject);
begin
// проверка, в каком режиме находится программа

// отправляем сообщение с сервера (он под номером 0, поскольку один)
ServerSocket.Socket.Connections.SendText("["+TimeToStr(Time)+"] "+NikEdit.Text+": "+TextEdit.Text)
else
// отправляем сообщение с клиента
ClientSocket.Socket.SendText("["+TimeToStr(Time)+"] "+NikEdit.Text+": "+TextEdit.Text);
// отобразим сообщение в ChatMemo
ChatMemo.Lines.Add("["+TimeToStr(Time)+"] "+NikEdit.Text+": "+TextEdit.Text);
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;
// выводим сообщение в ChatMemo
ChatMemo.Lines.Add("["+TimeToStr(Time)+"] Сессия закрыта.");
// возвращаем тэгу исходное значение
ClientBtn.Tag:=0;
// возвращаем исходную надпись клавиши
ClientBtn.Caption:="Подключиться";
end;
end;

Остается прописать процедуры на OnConnect , OnDisconnect , OnRead клиента ClientSocket . Сначала на чтение сообщения с сервера (OnRead ):


Socket: TCustomWinSocket);
begin
// добавим в ChatMemo пришедшее сообщение
ChatMemo.Lines.Add(Socket.ReceiveText());
end;

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;

Вот тот минимум, который нужно проделать. Остается внести еще некоторый ряд алгоритмов для проверки правильности ввода некоторых значений.
Возникает вопрос: а если нужно передать данные и совсем не строковые, а какой-нибудь массив? Для этого есть специальные команды. Давайте попробуем написать алгоритм отправки массива, как команды для некоторой игры.

Отправка массива данных

Воспользуемся той же формой чата, только добавим несколько компонентов чуть ниже. Пусть задача - управлять объектом типа Shape , менять тип геометрической фигуры, цвет, размеры. Поместим в форму компонент GroupBox , а в него Shape , их имена будут такими же. Для изменения типа геометрической фигуры используем список ComboBox , назовем его ShapeCBox . Сразу заполнять не будем, это сделаем в OnCreate формы. Далее понадобится такой же ComboBox для выбора цвета, и два Edit "а для указания размера фигуры (в случае с прямоугольником имеем два значения, на круг будем использовать одно первое). Назовем их ColorCBox , Value1Edit , Value2Edit , соответственно. Последним кинем в форму компонент Button , назовем его SendBufBtn , Caption изменим на "Отправить буфер ".
Немного о том, как представить вводимые данные в виде буфера данных. Нужно сразу определиться в последовательности, какое значение, за каким следует в буфере. Пусть первым будет тип фигуры, за ним цвет, а следом оба значения размера. Для этих целей следует использовать массив длиной 4 и типом Byte . Добавим в раздел var массив:

Buf: array of Byte;

С размерами фигуры все понятно, а вот для типа и цвета нужна "таблица истинности". Представим ее следующим образом:

параметр код
прямоугольник 0
круг 1
-------------------
красный 0
зеленый 1
синий 2

Этого вполне хватит для демонстрации. По желанию круг параметров можно расширить, ввести тип заливки, тип контура, смещение, или воспользоваться примером для других целей.
Пропишем заполнение списков в OnCreate формы:

procedure TForm1.FormCreate(Sender: TObject);
begin

// ...часть чата...

// заполнение списков
ShapeCBox.Items.Add("прямоугольник");
ShapeCBox.Items.Add("круг");
ColorCBox.Items.Add("красный");
ColorCBox.Items.Add("зеленый");
ColorCBox.Items.Add("синий");
end;

При нажатии клавиши "Отправить буфер " будем собирать данные с полей и формировать массив известной длины, а затем проверять на режим сервер/клиент и отправлять. Вот процедура SendBufBtn (OnClick) :

procedure TForm1.SendBufBtnClick(Sender: TObject);
begin
// соберем данные для отправки
Buf:=ShapeCBox.ItemIndex;
Buf:=ColorCBox.ItemIndex;
Buf:=StrToInt(Value1Edit.Text);
Buf:=StrToInt(Value2Edit.Text);
// проверяем режим программы
If ServerSocket.Active=True then
// отправим буфер с сервера (длина известна - 4)
ServerSocket.Socket.Connections.SendBuf(Buf,4)
else
// отправим буфер с клиента
ClientSocket.Socket.SendBuf(Buf,4);
// добавим в ChatMemo сообщение о передачи данных
ChatMemo.Lines.Add("["+TimeToStr(Time)+"] Данные переданы.");

Shape.Height:=Buf;
Shape.Width:=Buf;

If Buf>


Case Buf of
0: Shape.Brush.Color:=clRed;

2: Shape.Brush.Color:=clBlue;
end;
// изменить данные в полях
ShapeCBox.ItemIndex:=Buf;
ColorCBox.ItemIndex:=Buf;


end;

Немного изменим процедуру на чтение сообщения от клиента, выключим возможность приема сообщений и настроем на прием буфера (OnClientRead ):

procedure TForm1.ServerSocketClientRead(Sender: TObject;
Socket: TCustomWinSocket);
var
len: Byte;
begin
// добавим в ChatMemo клиентское сообщение


len:=Socket.ReceiveLength;
Socket.ReceiveBuf(Buf,len);
// применим изменения к своему Shape
Shape.Height:=Buf;
Shape.Width:=Buf;
If Buf>0 then Shape.Shape:=stCircle {круг}
else Shape.Shape:=stRectangle; {прямоуголник}
// выбор цвета по таблице истинности
Case Buf of
0: Shape.Brush.Color:=clRed;
1: Shape.Brush.Color:=clGreen;
2: Shape.Brush.Color:=clBlue;
end;
// изменить данные в полях
ShapeCBox.ItemIndex:=Buf;
ColorCBox.ItemIndex:=Buf;
Value1Edit.Text:=IntToStr(Buf);
Value2Edit.Text:=IntToStr(Buf);


end;

Осталось аналогичным образом изменить процедуру на чтение клиентом сообщения от сервера (OnRead ):

procedure TForm1.ClientSocketRead(Sender: TObject;
Socket: TCustomWinSocket);
var
len: Byte;
begin
// добавим в ChatMemo сообщение с сервера
// ChatMemo.Lines.Add(Socket.ReceiveText());
// принимаем буфер неизвестного размера
len:=Socket.ReceiveLength;
Socket.ReceiveBuf(Buf,len);
// применим изменения к своему Shape
Shape.Height:=Buf;
Shape.Width:=Buf;
If Buf>0 then Shape.Shape:=stCircle {круг}
else Shape.Shape:=stRectangle; {прямоуголник}
// выбор цвета по таблице истинности
Case Buf of
0: Shape.Brush.Color:=clRed;
1: Shape.Brush.Color:=clGreen;
2: Shape.Brush.Color:=clBlue;
end;
// изменить данные в полях
ShapeCBox.ItemIndex:=Buf;
ColorCBox.ItemIndex:=Buf;
Value1Edit.Text:=IntToStr(Buf);
Value2Edit.Text:=IntToStr(Buf);
// добавим в ChatMemo сообщение о приходе данных
ChatMemo.Lines.Add("["+TimeToStr(Time)+"] Пришли данные.");
end;

Это и все, что нужно сделать. Обратите внимание на то, что если не отключать принятие сообщения (Socket.ReceiveText() ) и попытаться одновременно принять и сообщение и буфер, то это приведет к потере данных одной из функций. Решить эти проблемы можно за счет перевода сообщения в формат буфера вот так:

For i:=1 to Length(TextEdit.Text) do
Buf:=Copy(TextEdit.Text,i,1);

Очевидно, что массив станет на одну ячейку больше и изменит свой тип на символьный или строковый. Buf при этом будет служить меткой, чем является пришедший буфер: сообщением или данными. В процедурах получения сообщений нужно сделать условие примерно так:

len:=Socket.ReceiveLength;
Socket.ReceiveBuf(Buf,len);
If Buf="t" then
Begin
… делать операцию по соединению в строку (через цикл)
end;
If Buf="c" then
Begin
… делать операцию по изменению параметров Shape
end;

Введение

Данная статья посвящена созданию приложений архитектуры клиент/сервер в Borland Delphi на основе сокетов ("sockets" - гнезда ). В отличие от предыдущей статьи на тему сокетов, здесь мы разберем создание серверных приложений.

Следует сразу заметить, что для сосуществования отдельных приложений клиента и сервера не обязательно иметь несколько компьютеров. Достаточно иметь лишь один, на котором Вы одновременно запустите и сервер, и клиент. При этом нужно в качестве имени компьютера, к которому надо подключиться, использовать хост-имя localhost или IP-адрес - 127.0.0.1 .

Итак, начнем с теории. Если Вы убежденный практик (и в глаза не можете видеть всяких алгоритмов), то Вам следует пропустить этот раздел.

Алгоритм работы сокетного сервера

Что же позволяет делать сокетный сервер?.. По какому принципу он работает?.. Сервер, основанный на сокетном протоколе, позволяет обслуживать сразу множество клиентов. Причем, ограничение на их количество Вы можете указать сами (или вообще убрать это ограничение, как это сделано по умолчанию). Для каждого подключенного клиента сервер открывает отдельный сокет, по которому Вы можете обмениваться данными с клиентом. Также отличным решением является создание для каждого подключения отдельного процесса (Thread).

Разберем схему подробнее:

  • Определение св-в Port и ServerType - чтобы к серверу могли нормально подключаться клиенты, нужно, чтобы порт, используемый сервером точно совпадал с портом, используемым клиентом (и наоборот). Свойство ServerType определяет тип подключения (подробнее см.ниже);
  • Открытие сокета - открытие сокета и указанного порта. Здесь выполняется автоматическое начало ожидания подсоединения клиентов (Listen );
  • Подключение клиента и обмен данными с ним - здесь подключается клиент и идет обмен данными с ним. Подробней об этом этапе можно узнать ниже в этой статье и в статье про сокеты (клиентская часть);
  • Отключение клиента - Здесь клиент отключается и закрывается его сокетное соединение с сервером;
  • Закрытие сервера и сокета - По команде администратора сервер завершает свою работу, закрывая все открытые сокетные каналы и прекращая ожидание подключений клиентов.

Следует заметить, что пункты 3-4 повторяются многократно, т.е. эти пункты выполняются для каждого нового подключения клиента.

Примечание : Документации по сокетам в Дельфи на данный момент очень мало, так что, если Вы хотите максимально глубоко изучить эту тему, то советую просмотреть литературу и электронную документацию по Unix/Linux-системам - там очень хорошо описана теория работы с сокетами. Кроме того, для этих ОС есть множество примеров сокетных приложений (правда, в основном на C/C++ и Perl).

Краткое описание компонента TServerSocket

Здесь мы познакомимся с основными свойствами, методами и событиями компонента TServerSocket .

Свойства Методы События
Socket - класс TServerWinSocket, через который Вы имеете доступ к открытым сокетным каналам. Далее мы рассмотрим это свойство более подробно, т.к. оно, собственно и есть одно из главных. Тип: TServerWinSocket ;
ServerType - тип сервера. Может принимать одно из двух значений: stNonBlocking - синхронная работа с клиентскими сокетами. При таком типе сервера Вы можете работать с клиентами через события OnClientRead и OnClientWrite . stThreadBlocking - асинхронный тип. Для каждого клиентского сокетного канала создается отдельный процесс (Thread). Тип: TServerType ;
ThreadCacheSize - количество клиентских процессов (Thread), которые будут кэшироваться сервером. Здесь необходимо подбирать среднее значение в зависимости от загруженности Вашего сервера. Кэширование происходит для того, чтобы не создавать каждый раз отдельный процесс и не убивать закрытый сокет, а оставить их для дальнейшего использования. Тип: Integer ;
Active - показатель того, активен в данных момент сервер, или нет. Т.е., фактически, значение True указывает на то, что сервер работает и готов к приему клиентов, а False - сервер выключен. Чтобы запустить сервер, нужно просто присвоить этому свойству значение True . Тип: Boolean ;
Port - номер порта для установления соединений с клиентами. Порт у сервера и у клиентов должны быть одинаковыми. Рекомендуются значения от 1025 до 65535, т.к. от 1 до 1024 - могут быть заняты системой. Тип: Integer ;
Service - строка, определяющая службу (ftp , http , pop , и т.д.), порт которой будет использован. Это своеобразный справочник соответствия номеров портов различным стандартным протоколам. Тип: string ;
Open - Запускает сервер. По сути, эта команда идентична присвоению значения True свойству Active ;
Close - Останавливает сервер. По сути, эта команда идентична присвоению значения False свойству Active .
OnClientConnect - возникает, когда клиент установил сокетное соединение и ждет ответа сервера (OnAccept );
OnClientDisconnect - возникает, когда клиент отсоединился от сокетного канала;
OnClientError - возникает, когда текущая операция завершилась неудачно, т.е. произошла ошибка;
OnClientRead - возникает, когда клиент передал берверу какие-либо данные. Доступ к этим данным можно получить через пеаедаваемый параметр Socket: TCustomWinSocket ;
OnClientWrite - возникает, когда сервер может отправлять данные клиенту по сокету;
OnGetSocket - в обработчике этого события Вы можете отредактировать параметр ClientSocket ;
OnGetThread - в обработчике этого события Вы можете определить уникальный процесс (Thread) для каждого отдельного клиентского канала, присвоив параметру SocketThread нужную подзадачу TServerClientThread;
OnThreadStart , OnThreadEnd - возникает, когда подзадача (процесс, Thread) запускается или останавливается, соответственно;
OnAccept - возникает, когда сервер принимает клиента или отказывает ему в соединении;
OnListen - возникает, когда сервер переходит в режим ожидания подсоединения клиентов.

TServerSocket.Socket (TServerWinSocket)

Итак, как же сервер может отсылать данные клиенту? А принимать данные? В основном, если Вы работаете через события OnClientRead и OnClientWrite , то общаться с клиентом можно через параметр ClientSocket (TCustomWinSocket). Про работу с этим классом можно прочитать в статье про клиентские сокеты, т.к. отправка/посылка данных через этот класс аналогична - методы (Send/Receive)(Text,Buffer,Stream). Также и при работе с TServerSocket.Socket. Однако, т.к. здесь мы рассматриваем сервер, то следует выделить некоторые полезные свойства и методы:

  • ActiveConnections (Integer ) - количество подключенных клиентов;
  • ActiveThreads (Integеr ) - количество работающих процессов; Connections (array ) - массив, состоящий из отдельных классов TClientWinSocket для каждого подключенного клиента. Например, такая команда:
    ServerSocket1.Socket.Connections.SendText("Hello!");
    отсылает первому подключенному клиенту сообщение "Hello!". Команды для работы с элементами этого массива - также (Send/Receive)(Text,Buffer, Stream);
  • IdleThreads (Integer ) - количество свободных процессов. Такие процессы кэшируются сервером (см. ThreadCacheSize );
  • LocalAddress , LocalHost , LocalPort - соответственно - локальный IP-адрес, хост-имя, порт;
  • RemoteAddress , RemoteHost , RemotePort - соответственно - удаленный IP-адрес, хост-имя, порт;
  • Методы Lock и UnLock - соответственно, блокировка и разблокировка сокета.

Практика и примеры

А теперь рассмотрим вышеприведенное на конкретном примере. Скачать уже готовые исходники можно, щелкнув .

Итак, рассмотрим очень неплохой пример работы с TServerSocket (этот пример - наиболее наглядное пособие для изучения этого компонента). В приведенных ниже исходниках демонстрируется протоколирование всех важных событий сервера, плюс возможность принимать и отсылать текстовые сообщения:

Пример 1. Протоколирование и изучение работы сервера, посылка/прием сообщений через сокеты.

{... Здесь идет заголовок файла и определение формы TForm1 и ее экземпляра Form1} {Полный исходник смотри } procedure TForm1.Button1Click(Sender: TObject); begin {Определяем порт и запускаем сервер} ServerSocket1.Port:= 1025; {Метод Insert вставляет строку в массив в указанную позицию} Memo2.Lines.Insert(0,"Server starting"); ServerSocket1.Open; end; procedure TForm1.Button2Click(Sender: TObject); begin {Останавливаем сервер} ServerSocket1.Active:= False; Memo2.Lines.Insert(0,"Server stopped"); end; procedure TForm1.ServerSocket1Listen(Sender: TObject; Socket: TCustomWinSocket); begin {Здесь сервер "прослушивает" сокет на наличие клиентов} Memo2.Lines.Insert(0,"Listening on port "+IntToStr(ServerSocket1.Port)); end; procedure TForm1.ServerSocket1Accept(Sender: TObject; Socket: TCustomWinSocket); begin {Здесь сервер принимает клиента} Memo2.Lines.Insert(0,"Client connection accepted"); end; procedure TForm1.ServerSocket1ClientConnect(Sender: TObject; Socket: TCustomWinSocket); begin {Здесь клиент подсоединяется} Memo2.Lines.Insert(0,"Client connected"); end; procedure TForm1.ServerSocket1ClientDisconnect(Sender: TObject; Socket: TCustomWinSocket); begin {Здесь клиент отсоединяется} Memo2.Lines.Insert(0,"Client disconnected"); end; procedure TForm1.ServerSocket1ClientError(Sender: TObject; Socket: TCustomWinSocket; ErrorEvent: TErrorEvent; var ErrorCode: Integer); begin {Произошла ошибка - выводим ее код} Memo2.Lines.Insert(0,"Client error. Code = "+IntToStr(ErrorCode)); end; procedure TForm1.ServerSocket1ClientRead(Sender: TObject; Socket: TCustomWinSocket); begin {От клиента получено сообщение - выводим его в Memo1} Memo2.Lines.Insert(0,"Message received from client"); Memo1.Lines.Insert(0,"> "+Socket.ReceiveText); end; procedure TForm1.ServerSocket1ClientWrite(Sender: TObject; Socket: TCustomWinSocket); begin {Теперь можно слать данные в сокет} Memo2.Lines.Insert(0,"Now can write to socket"); end; procedure TForm1.ServerSocket1GetSocket(Sender: TObject; Socket: Integer; var ClientSocket: TServerClientWinSocket); begin Memo2.Lines.Insert(0,"Get socket"); end; procedure TForm1.ServerSocket1GetThread(Sender: TObject; ClientSocket: TServerClientWinSocket; var SocketThread: TServerClientThread); begin Memo2.Lines.Insert(0,"Get Thread"); end; procedure TForm1.ServerSocket1ThreadEnd(Sender: TObject; Thread: TServerClientThread); begin Memo2.Lines.Insert(0,"Thread end"); end; procedure TForm1.ServerSocket1ThreadStart(Sender: TObject; Thread: TServerClientThread); begin Memo2.Lines.Insert(0,"Thread start"); end; procedure TForm1.Button3Click(Sender: TObject); var i: Integer; begin {Посылаем ВСЕМ клиентам сообщение из Edit1} for i:= 0 to ServerSocket1.Socket.ActiveConnections-1 do begin ServerSocket1.Socket.Connections[i].SendText(Edit1.Text); end; Memo1.Lines.Insert(0,"< "+Edit1.Text); end;

Приемы работы с TServerSocket (и просто с сокетами)

Хранение уникальных данных для каждого клиента.

Наверняка, если Ваш сервер будет обслуживать множество клиентов, то Вам потребуется хранить какую-либо информацию для каждого клиента (имя, и др.), причем с привязкой этой информации к сокету данного клиента. В некоторых случаях делать все это вручную (привязка к handle сокета, массивы клиентов, и т.д.) не очень удобно. Поэтому для каждого сокета существует специальное свойство - Data . На самом деле, Data - это всего-навсего указатель. Поэтому, записывая данные клиента в это свойство будьте внимательны и следуйте правилам работы с указателями (выделение памяти, определение типа, и т.д.)!

Посылка файлов через сокет.

Здесь мы рассмотрим посылку файлов через сокет (по просьбе JINX-а) :-). Итак, как же послать файл по сокету? Очень просто! Достаточно лишь открыть этот файл как файловый поток (TFileStream) и отправить его через сокет (SendStream)! Рассмотрим это на примере:

Нужно заметить, что метод SendStream используется не только сервером, но и клиентом (ClientSocket1.Socket.SendStream(srcfile) )

Почему несколько блоков при передаче могут обьединяться в один

Это тоже по просьбе JINX-а:-). За это ему огромное спасибо! Итак, во-первых, надо заметить, что посылаемые через сокет данные могут не только объединяться в один блок, но и разъединяться по нескольким блокам. Дело в том, что сокет - обычный поток, но в отличие, скажем, от файлового (TFileStream), он передает данные медленнее (сами понимаете - сеть, ограниченный трафик, и т.д.). Именно поэтому две команды:
ServerSocket1.Socket.Connections.SendText("Hello, ");
ServerSocket1.Socket.Connections.SendText("world!");
совершенно идентичны одной команде:
ServerSocket1.Socket.Connections.SendText("Hello, world!");

И именно поэтому, если Вы отправите через сокет файл, скажем, в 100 Кб, то тому, кому Вы посылали этот блок, придет несколько блоков с размерами, которые зависят от трафика и загруженности линии. Причем, размеры не обязательно будут одинаковыми. Отсюда следует, что для того, чтобы принять файл или любые другие данные большого размера, Вам следует принимать блоки данных, а затем объединять их в одно целое (и сохранять, например, в файл). Отличным решением данной задачи является тот же файловый поток - TFileStream (либо поток в памяти - TMemoryStream). Принимать частички данных из сокета можно через событие OnRead (OnClientRead), используя универсальный метод ReceiveBuf . Определить размер полученного блока можно методом ReceiveLength . Также можно воспользоваться сокетным потоком (см. статью про TClientSocket). А вот и небольшой примерчик (приблизительный):

Как следить за сокетом

Это вопрос сложный и требует долгого рассмотрения. Пока лишь замечу, что созданный Вашей программой сокет Вы можете промониторить всегда:-). Сокеты (как и большинство объектов в Windows) имеют свой дескриптор (handle), записанный в свойстве Handle. Так вот, узнав этот дескриптор Вы свободно сможете управлять любым сокетом (даже созданным чужой программой)! Однако, скорее всего, чтобы следить за чужим сокетом, Вам придется использовать исключительно функции WinAPI Sockets.

Эпилог

В этой статье отображены основные приемы работы с компонентом TServerSocket в Дельфи и несколько общих приемов для обмена данными по сокетам. Если у Вас есть вопросы - скидывайте их мне на E-mail: [email protected] , а еще лучше - пишите в конференции этого сайта (Delphi. Общие вопросы), чтобы и другие пользователи смогли увидеть Ваш вопрос и попытаться на него ответить!

Карих Николай (Nitro ). Московская область, г.Жуковский

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

КОНТРОЛЬНАЯ РАБОТА

Разработка приложения «клиент-сервер» в D elphi

Обзор компонент

Для работы с ADO на вкладке компонентов ADO есть шесть компонентов: TADOConnection, TADOCommand, TADODataSet, TADOTable, TADOQuery, TADOStoredProc.

Рис. 1. Палитра компонент ADO

TADOConnection используется для указания базы данных и работы транзакциями.

TADOTable - таблица доступная через ADO.

TADOQuery - запрос к базе данных. Это может быть как запрос, в результате которого возвращаются данные и базы (например, SELECT), так и запрос, не возвращающий данных (например, INSERT).

TADOStoredProc - вызов хранимой процедуры.

TADOCommand и TADODataSet являются наиболее общими компонентами для работы с ADO, но и наиболее сложными в работе. Оба компонента позволяют выполнять команды на языке провайдера данных (так в ADO называется драйвер базы данных).

Разница между ними в том, что команда, исполняемая через TADODataSet, должна возвращать набор данных и этот компонент позволяет работать с ними средствами Delphi (например, привязать компонент типа TDataSource). А компонент TADOCommand позволяет исполнять команды не возвращающие набор данных, но не имеет штатных средств Delphi для последующего использования возвращенного набора данных.

Очевидно, что все компоненты должны связываться с базой данных. Делается это двумя способами либо через компонент TADOConnection либо прямым указанием базы данных в остальных компонентах. К TADOConnection остальные компоненты привязываются с помощью свойства Connection, к базе данных напрямую через свойство ConnectionString.

программирование база данные алгоритм

Таблица 1. Основные компоненты вкладки ADO

Название

Основные свойства

Комментарии

Отвечает за связь с БД

ConnectionString

Содержит настройки для соединения с БД.

используется для указания того, нужно ли выводить диалоговое окно, предназначенное для ввода идентификатора пользователя и пароля при соединении с базой данных;

указывает активность соединения: True означает соединение открыто, False-закрыто;

Основные методы

Комментарии

используется для запуска новой транзакции и возвращает уровень вложенных транзакций;

закрывает открытое в текущий момент соединение

записывает в базу данных все изменения, произведенные текущей транзакцией, и завершает ее

используется для выполнения запросов или хранимых процедур. Исполняемый оператор передается данному методу через параметр;

открывает соединение с источником данных, заданным в ConnnectionString

отменяет все изменения, произведенные со времени последнего вызова метода BeginTrans, и завершает транзакцию без сохранения изменений.

Основные свойства

Комментарии

TADOQuery Предназначен для работы с запросами

Предназначен для работы с хранимыми процедурами

Cодержит имя компонента ADOConnection

Содержит имя хранимой процедуры

Алгоритм работы

1. Создаем новый DataModule, Выбираем пункт меню File\New\DataModule. Называем его DM.

Кидаем на DM компоненты ADOConnection, ADOQuery и три штуки ADOStoredProcedure. Как это выглядит смотри на рис.2

Рис.2 Вид DM

2. Настроить свойства компонентов

ADOConnection - смотри методичку.

ADOQuery 1 переименовываем в QVrash и в свойстве SQL пишем запрос:

Select * from Vrash

Свойство Connection - ADOConnectionl.

Создать статические поля для QVrash.

Для компонента ADOStoredProcl устанавливаем следующие свойства

* Name - ASPInsVrash

* Connection - ADOConnection 1

* ProcedureName - MainInsert; 1

Если щелкнешь на свойстве Parameters, то увидишь те параметры, которые мы будем передавать в процедуру.

Для компонента ADOStoredProc2 устанавливаем следующие свойства

* Name - ASPEdVrash

* Connection - ADOConnectionl

* ProcedureName - MainEdit; 1

Для компонента ADOStoredProc3 устанавливаем следующие свойства

* Name - ASPDelVrash

* Connection - ADOConnectionl

* ProcedureName - MainDelete; 1

На форму 1 разместить компоненты DBGrid, два Button, DataSource, MainMenu

Рис3. Форма 1

Кнопки переименовать в соответствии с рисунком

Для кнопки Обновить код

DM.QVrash.Close; //обновить н/д

Создаём меню

Рис. 3. Меню «Правка»

Создаём форму для добавления/редактирования: Компоненты EDIT

Программный код для пункта меню на форме 1 - ДОБАВЛЕНИЕ

Form3.Edit1.text:="";

Form3.Edit2.text:="";

Form3.Edit3.text:="";

Form3.Edit4.text:="";

Form3.showmodal;

Программный код для кнопки на форме 3 - ВСТАВКА

B egin

showmessage("Заполнены не все поля");

E nd;

T ry

DM.ADOConnection1.BeginTrans;

with DM.ASPInsVrash do

Parameters.ParamByName("@ ID_Physician").Value:=StrToInt(Edit1.text);

Parameters.ParamByName("@ YYYY`).Value:=Edit2.Text;

Parameters.ParamByName("@YYYY ").Value:=Edit3.Text;

Parameters.ParamByName("@YYYY ").Value:=Edit4.text;

DM. ASPInsVrash.ExecProc;

E xcept

DM. QVrash.Close;

DM. QVrash.Open;

Размещено на http://www.allbest.ru/

Программный код для пункта меню на форме 1 - РЕДАКТИРОВАНИЕ

B egin //Редактирование

n:=DM. QVrash.XXXXX.Value;

DM. QVrash.Close;

DM. QVrash.Open;

DM. QVrash.Locate(XXXX,n,);

Form3.Label6.Caption:=IntToStr(DM.QVrash.XXXXXvalue);

Form3.Edit1.text:=inttostr(DM.QVrashYYYYYY.Value);

Form3.Edit2.text:=inttostr(DM.QVrashYYYYY.Value);

Form3.Edit3.text:=floattostr(DM.QVrashYYYYY.Value);

Form3.Edit4.text:=DateToStr(DM.QVrash.YYYY.Value);

Form3.Showmodal;

DM. QVrash.Close;

DM. QVrash.Open;

Программный код для кнопки на форме 3 - РЕДАКТИРОВАНИЕ

B egin

If (Edit1.text="") or (Edit2.Text="")or (Edit3.Text="") or (Edit4.Text="") then

B egin

ShowMessage("Заполнены не все поля");

E nd ;

T ry

DM.ADOConnection1.BeginTrans;

with DM.ASPEdVrash do

Parameters.ParamByName("@XXXX).Value:= DM.a QVrash.XXXXX.value;

Parameters.ParamByName("@YYYY).Value:=Edit1.text;

Parameters.ParamByName("@YYYYY).Value:=Edit2.Text;

Parameters.ParamByName("@YYYY").Value:=Edit3.Text;

Parameters.ParamByName("@YYYY).Value:=StrToDate(Edit4.text);

DM.ASPEd.ExecProc;

DM.ADOConnection1.CommitTrans;

E xcept

DM.ADOConnection1.RollbackTrans;

ShowMessage("Невозможно выполнить. Повторите.");

DM. QVrash.Close;

Программный код для пункта меню на форме 1 - УДАЛЕНИЕ

Begin // Удаление

if MessageDlg("Вы уверены что хотите удалить запись?", mtConfirmation,,0)=mrYes then

DM.ASPDel.Parameters.ParamByName("@XXXX).Value:=DM.ADOQuery1XXXXX.Value;

T ry

DM.ADOConnection1.BeginTrans;

DM.ASPDelVrash.ExecProc;

DM.ADOConnection1.CommitTrans;

E xcept

ShowMessage("Удаление не прошло!"+#13+"Запись заблокирована,либо уже удалена!");

DM.ADOConnection1.RollbackTrans;

E xit;

DM. QVrash.Close;

DM. QVrash.Open;

Размещено на Allbest.ru

Подобные документы

    Разработка информационной системы административного управления. Выбор языка и среды программирования. Структура взаимодействия информации. Требования к программно-аппаратному окружению. Создание программы в Delphi и связывание ее с базой данных.

    курсовая работа , добавлен 08.10.2015

    Borland Delphi 7 как универсальный инструмент разработки, применяемый во многих областях программирования, функции: добавление информации об абитуриентах в базу данных, формирование отчетов. Рассмотрение и характеристика основных компонентов Delphi.

    контрольная работа , добавлен 18.10.2012

    Разработка программного обеспечения для работы с информацией и ее обработкой на языке программирования Delphi. Описание алгоритмов процедуры работы со стеком - добавление, удаление элементов, редактирование записи. Инструкция по использованию программы.

    курсовая работа , добавлен 06.02.2013

    Рассмотрение особенностей среды программирования Delphi, анализ клиент-серверной версии. Знакомство с библиотекой визуальных компонентов. Основные функции интеллектуального редактора. Характеристика требований к базам данных. Функции программы "Магистр".

    дипломная работа , добавлен 10.03.2013

    Характеристика системы программирования. Главные составные части Delphi. Интерфейс программного приложения. Результаты работы программы. Руководство системного программиста и оператора. Язык программирования Delphi, среда компилятора Borland 7.0.

    курсовая работа , добавлен 29.05.2013

    Написание программы для работы с клиентами средствами языка Delphi, которая предусматривает ввод, редактирование и удаление информации. Разработка алгоритма решения задачи, описание переменных, вспомогательных процедур, входных и выходных данных.

    курсовая работа , добавлен 21.09.2010

    Общая характеристика системы программирования Delphi, а также принципы создания ее компонентов. Описание значений файлов приложения, созданного с помощью Delphi. Структура и свойства библиотеки визуальных компонентов (Visual Component Library или VCL).

    отчет по практике , добавлен 07.12.2010

    Проектирование и создание пользовательского интерфейса и визуального программирования в среде Delphi. Система управления базой данных. Локальные и глобальное пользовательские представления. Анализ предметной области. Назначение форм и компонентов.

    курсовая работа , добавлен 07.03.2014

    История интегрированной среды разработки, версии Delphi. Организация библиотеки компонентов. Страница Additional, ряд часто используемых компонентов общего назначения. Исполняемый файл программы "Архиватор текстовых файлов", интерфейс приложения.

    курсовая работа , добавлен 16.05.2017

    Разработка программных продуктов на языке программирования Borland Delphi. Применяемые таблицы и связи между ними. Пользовательский интерфейс работы с базой данных. Алгоритм работы программы "Футбольные команды и игроки". Защита от ввода неверных данных.

Итак создадим новую БД, назовем ее Test и сохраним ее в папке C:\ClientServer\Server\Data (процесс создания новой БД в Аксесе я, по понятным, причинам опускаю, и приведу только структуру таблиц - входящих в нашу базу).

1. Таблица первая, назовем ее First

2. Таблица вторая, а назовем ее уже по хитрому - Second

Ну вот, как бы, с базой и все.

Программа

2.1. Приложение сервер.

Созданим новое приложение и сохраним его под именем Server в папке C:\ClientServer\Server. Добавим в приложение Remote Data Module с вкладки Multitier репозитария (Рис. 1).

Рис. 1 Репозитарий с выделенным Remote Data Module.

При добавлении RDM мастер добавления запросит параметры данного модуля - там введем имя нашего сервера "Test".


Все остальное оставим без изменений. После нажатия кнопки "Ок" в проекте появится форма подобная обычному дата модулю с именем Test. Сохраним ее под именем RDMFrm.pas .

Выложим на нее компоненты ADOConnection (одна штука), ADOTable и DataSetProvider (по две штуки). Компоненты ADOTable и DataSetProvider обзовем - adotFirst, adotSecond, dspFirst и dspSecond соответственно. По двойному клику на компоненте ADOConnection вывалится мастер Connection String. Выбераем пункт "Use Connection String" и нажимаем кнопочку Build. В появившемся окне "Свойства связи с данными" выберем пункт "Microsoft Jet 4.0 OLE DB Provider" и нажмем кнопку "Далее". На владке "Подключение" введем путь к БД, нажмем кнопочку "Проверить подключение" и получим сообщение что проверка подключения выполнена. После чего закроем мастер путем нажатия на кнопке ОК. Еще у компонента ADOConnection свойство есть LoginPrompt, которое установим в False и Connected - True. У компонетов ADOTable свойство Connection установим равным ADOConnection путем выбора из выпадающего списка. Свойство TableName установим в First и Second соответсвенно. Свойство CursorType - ctDynamic, свойство TableDirect - True. Вызвать FieldsEditor и добавить туда все поля.

У компонентов DataSetProvider:

Вот на этом процесс создания примитивного MIDAS сервера можно считать завершенным. Чтобы зарегистрировать сервер в подсистеме DCOM нужно запустить приложение с параметром /regserver - Server.exe /regserver.
И последний штрих, это приложение СокетСервер от Борланда, которое находится в папке Delphi?/Bin/scktsvr.exe. Для того чтобы клиент мог видеть сервер нужно запустить scktsvr.exe (если у вас установлена Win NT/w2k/XP то можно это приложение зарегистрировать как службу, для этого его нужно запустить с параметром scktsvr.exe /install)

2.2. Приложение клиент

Создадим новое приложение, и сохраним его в папке C:\ClientServer\Client под именем Client. Разместим на главной форме по два компонента ClientDataSet(обзовем их cdsFirst, cdsSecond), DataSource(обзовем их dsFirst, dsSecond), DBGrid(обзовем их dbgFirst, dbgSecond) один компонент SocetConnection.

SocetConnection

Если вы все сделали правильно, то при установке свойства Connected в True, должно запустится приложение сервер.

ClientDataSet

Запустим Редактор Полей (двойной клик по компоненту) и занесем туда все поля. Для компонентов ClientDataSet напишем обработчик sdsAfterPost

Procedure TMainForm.cdsAfterPost(DataSet: TDataSet);
begin
with (DataSet as TClientDataSet) do begin
if State in then
try
ApplyUpdates(0);
Refresh;
except
raise EDatabaseError.Create("Ошибочка вышла");
end;
end;
end;

И подставим его на обработку события AfterPost обоих ClientDataSet"ов.

DataSource

Свойство Значение
DataSet cdsFirst и cdsSecond соответственно

DBGrid

Свойство Значение
DataSource dsFirst и dsSecond соответственно

Запустим Редактор колонок (двойной клик по компоненту) и занесем туда все поля.
Положим на форме возле Гридов по кнопке и в обработчик onClick запишем:

  • для dbgFirst - cdsFirst.Post
  • dbgSecond - cdsSecond.Post

Все. Закрываем Делфи (для чистоты эксперимента). И запускаем приложение клиент. Если вы все сделали правильно то вы должны увидеть нечто подобное этому


Если статья получит отзывы и заслужит интерес у читателей, то продолжение не заставит себя долго ждать.