Сегодня остается весьма актуальной тема в сфере информационных технологий, посвященная разработке и развертыванию архитектуры «клиент - сервер». Связано это с тем, что информационные системы различных масштабов находят все большее применение в различных областях. Кроме того, структура любой базы данных создается по определенным параметрам, причем таким образом, что работать с ней в штатном режиме возможно только с применением специальных приложений. Поэтому реализация архитектуры «клиент - сервер» - очень важная задача, возникающая перед отделами информационных технологий на различных объектах, успешное решение которой позволит работать предприятию оптимально и бесперебойно.

Сервер на основе сокетов

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

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

Схема работы архитектуры «Клиент - сервер»

Рассмотрим более подробно схему реализации архитектуры, последовательно анализируя часто используемые определения:

1. Свойства ServerType и Port. Для того чтобы пользователь имел возможность подключиться к серверу, важно гарантировать, чтобы порт, используемый как сервером, так и клиентом совпадал. Именно назначение данного параметра производится в свойстве Port. Тип подключения определяется параметром ServerType.

2. Процесс открытия сокета. Для начала сессии «клиент - сервер» важно открыть сокет и порт. Используется свойство Listen.

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

Применение TServerSocket в приложениях на языке Delphi «Клиент - сервер»

Для отправления и приема информации от пользователя используются события OnClientWrite и OnClientRead. При этом взаимодействовать с пользователем можно через такой параметр, как ClientSocket. При работе часто используются следующие методы и свойства:

Количество подключенных в текущий момент пользователей;

Число активных процессов;

Число свободных процессов;

Порт, хост-имя и локальный IP-адрес;

Разблокировка и блокировка сокета.

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

Документация

На сегодняшний день литературы по разработке приложений «клиент - сервер» Delphi, связанной с разработкой и применением сокетов, представлено очень мало. Поэтому для детального изучения данного аспекта можно обратиться к документации по Linux и Unix-системам, т.к. в ней достаточно подробно рассматривается технология развертывания приложений с применением сокетов, однако, как правило, на языках Perl или С++.

Для разработки приложения «Телефонный справочник» используется среда визуального программирования Delphi 7. Проект программы будет содержать три окна:

  • 1. fMain - главная форма программы.
  • 2. fEditor - форма добавления/редактирования записей.
  • 3. fDM - модуль данных.

На форму fDM добавим компонент ADOConnection, 1 компонент типа ADOTable, 1 компонент типа DataSource. Щелкните дважды по свойству ConnectionString компонента ADOConnection. Откроется окно подключения компонента к ADO (Рис.2.):

Puc. 2. Окно подключения к ADO.

Нажмем кнопку Build. Открывается новое окно (Рис.3.), содержащее настройки подключения:

Рис. 3.

Выбрать Поставщик данных - Microsoft OLE DB Provider for SQL Server. На вкладке Подключение: выбрать имя сервера, на котором расположена ваша БД. Теперь свойство Connected переведем в True. У ADOTable изменим следующие свойства: Connection на ADOConnection1; TableName на Abonent; Active на True. У DataSource изменить следующие свойства (это будет ссылка на таблицу): DataSet на ADOTable.

Перейдем на главную форму. Выберем команду File -> Use Unit и подключите к ней модуль DM. Теперь мы сможем видеть таблицы из главной формы.

Разместим на главной форме компоненты. Они находятся в области главного окна интегрированной среды, которая называется палитрой компонентов (Рис.4.).

Рис. 4.

На главную форму fMain добавим три кнопки Button (Рис.5). Они предназначены для редактирования текущей записи, добавления новой и удаления.

Рис.5.

На вкладке DataControls сосредоточены визуальные (видимые пользователю) компоненты отображения данных, такие как DBGrid (сетка, отображающая все данные в виде таблицы, и позволяющая редактировать их), DBEdit (поле редактирования данных, предназначенная для ввода или редактирования одного поля записи, то есть, ячейки таблицы), DBMemo (для редактирования MEMO-полей) и т.д. Единственным исключением является компонент DBNavigator. Этот компонент предназначен не для отображения данных, а для перемещения по записям набора данных, для вставки новой записи или удаления старой. Добавим и выделим сетку DBGrid, в ее свойстве DataSource выберите fDM.ADOTable. Сетка среагирована, и мы видим названия полей (Рис.6.).

Рис. 6.

Теперь создадим фильтр записей по различным критериям. Для этого добавим на форму два компонента GroupBox. В инспекторе объекта (Рис.7.) в свойстве Caption компонента GroupBox1 введем «Фильтр по одному полю», а у компонента GroupBox2 «Фильтр по нескольким полям».

Рис.7.

На GroupBox1 добавим 3 метки Label, 1 ComboBox, 1 Edit, и 2 RadioButton. У Label1 в свойстве Caption напишем «Поиск в», у Label2 «Введите данные», у Label3 «Способ». В свойстве Items компонента ComboBox введем две строки: фамилия, телефон (Рис.8.).

Рис.8.

У RadioButton свойство Caption «С начала строки» и «Любое вхождение». На второй GroupBox добавим три метки Label со свойством Caption: Фамилия, Имя, Телефон. Три компонента Edit и одну кнопку Button со свойством Caption «Найти». И добавим на форму еще одну кнопку Button, для снятия фильтра.

Теперь сделаем окно редактора данных. Создадим новую форму (File -> New -> Form). Ее свойство Name переименуем в fEditor. Командой File -> Use Unit подключим к форме модуль данных DM. Теперь нам нужно установить на форму такие компоненты: 8 Label, 7 DBEdit, 1 DBComboBox.

Доработаем компонент DBComboBox. Щелкним дважды по его свойству Items, открыв редактор. В нем введем три строки:

Улица

Переулок

Проспект

Сохраним текст, нажав кнопку ОК.

Подключим компоненты контроля. Удерживая , выделим все компоненты контроля (все компоненты, кроме Label). В их свойстве DataSource выберем fDM.DSLichData, подключив компоненты к нужному набору данных (таблице). Снимем общее выделение, и выделим первый DBEdit. В его свойстве DataField выберем поле "Фамилия". Это свойство подключает выбранный компонент к определенному полю таблицы. Таким же образом подключим к соответствующим полям остальные компоненты.

В правой нижней части установим навигационный компонент DBNavigator с вкладки Data Controls. Этот компонент предназначен для перемещения по записям, включения режима редактирования записи, сохранения или отмены сделанных изменений, добавления новой записи или удаления существующей. В его свойстве DataSource выбираем fDM.DSLichData, чтобы подключить компонент к главной таблице. Нам нужна от этого компонента только возможность перехода на начало или конец таблицы, на следующую или предыдущую запись. Поэтому раскроем его свойство VisibleButtons (видимость кнопок компонента) и переведем в False все кнопки, кроме nbFirst, nbPrior, nbNext и nbLast. Нажатие на эти кнопки приведет к вызову соответствующих методов компонента ADOTable. Эти методы делают следующее:

First - переход на первую запись таблицы. Prior - переход на предыдущую запись. Next - переход на следующую запись. Last - переход на последнюю запись.

Когда у DBNavigator останется всего четыре кнопки, эти кнопки окажутся вытянутыми. Уменьшим ширину компонента, чтобы кнопки приняли более привычный вид.

fDM.TLichData.Append;

DBEdit1.SetFocus;

Методом Append добавляем в таблицу новую запись. Далее мы переводит фокус ввода на DBEdit1, чтобы пользователю не пришлось делать это самому.

if fDM.TLichData.Modified then

fDM.TLichData.Post;

Здесь сохраняем изменения в таблице, если они были, и закрываем окно.

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

fDM.TLichData.Append;

fEditor.ShowModal;

Сгенерируем процедуру OnClick для кнопки "Редактировать". Тут будет лишь одна строчка кода:

fEditor.ShowModal;

В результате откроется окно редактора, и компоненты будут отображать данные текущей записи.

Код для кнопки «Удалить»:

fDM.TLichData.Delete;

Перейдем к фильтрации данных. В данном фильтре мы выбираем поле для поиска, выбираем способ и вводим данные.

Сгенерируем событие OnChange у компонента Edit2.

if ((Length(Edit2.Text) > 0)and(Length(ComboBox1.Text) > 0)) then

fDM.TLichData.Filtered:=false;

if RadioButton1.Checked then

fDM.TLichData.Filter:=Combobox1.Text + " LIKE "+ #39 + Edit2.Text + "%" + #39

fDM.TLichData.Filter:=Combobox1.Text + " LIKE "+ #39+ "%" + Edit2.Text + "%" + #39;

fDM.TLichData.Filtered:=true;

else fDM.TLichData.Filtered:=false;

Строка-условие фильтра означает следующее - выбрать те записи из столбца, которые начинаются с тех же символов, что и набраны в Edit2.Text.

Ключевое слово LIKE позволяет по заданному шаблону сравнивать строки. При этом нужно знать следующее:

символ "%" (процент) - заменяет любую последовательность из символов.

Знак #39 - означает номер символа " (одинарная кавычка) в кодовой таблице ASCII. Дело в том, что значение на фильтрацию нужно указывать в одинарных кавычках, а так как одинарные кавычки используются в Delphi для ограничения строк, то чтобы внутри строки поставить одинарную кавычку, её нужно поставить дважды.

Фильтр по нескольким полям. Сгенерируем событие OnClick у кнопки Найти.

procedure TfMain.Button3Click(Sender: TObject);

fDM.TLichData.filtered:=false;

if length(edit4.text) > 0 then

filtr:= "ФАМИЛИЯ LIKE "+ #39 + Edit4.Text + "%" + #39;

if length(edit5.text) > 0 then

if length(filtr) >

filtr:=filtr + add + "ИМЯ LIKE "+ #39 + Edit5.Text + "%" + #39;

if length(edit6.text) > 0 then

if length(filtr) > 0 then add:= " and " else add:="";

filtr:=filtr + add + "телефон LIKE "+ #39 + Edit6.Text + "%" + #39;

if length(filtr) > 0 then

fDM.TLichData.Filter:= filtr;

fDM.TLichData.filtered:=true;

Showmessage("Все поля пусты!");

Для отключения фильтра нажимаем на кнопку «Снять фильтр». Код у нее такой:

fDM.TLichData.Filter:= "";

fDM.TLichData.Filtered:= false;

Введение

Данная статья посвящена созданию приложений архитектуры клиент/сервер в 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 ). Московская область, г.Жуковский

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

Для начала давайте найдем необходимые компоненты TServerSocket и TClientSocket. Чаще всего они не входят в стандартный пакет установки Delphi, но их можно установить дополнительно. Для этого запустим Delphi, зайдем в «Component/Install Packages», затем нажмем кнопку «Add». В открывшемся диалоговом окне нужно найти файл «dclsocketsXX.bpl» (он лежит в папке bin, которая находится в папке с Delphi), где XX — это числовой номер версии вашего Delphi. Находим файл, нажимаем «Открыть», а затем в окне «Install Packages» нажимаем «OK».

Теперь, во вкладке «Internet» появились два компонента — TServerSocket и TClientSocket, работу с которыми мы и будем рассматривать в этом уроке.

Не сложно догадаться по названиям самих компонентов, что компонент TClientSocket выполняет роль клиента в сети, который подсоединяется к серверу с компонентом TServerSocket.

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

Сервер служит обычно для сетевого взаимодействия клиентов. Таким образом клиент может отсылать какую-либо информацию на сервер, а сервер будет эту информацию отсылать остальным клиентам.

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

Такие сетевые приложения, в работе которых участвуют клиент и сервер, называются клиент-серверными приложениями.

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

Для начала создадим сервер, который должен принимать сообщение и отсылать его всем клиентам обратно.

Создадим новое приложение в Delphi, поместим на него компонент TServerSocket и в его параметрах укажем следующие значения:

Active = false
Name = srv
Port = 22500
ServerType = stNonBlocking

Параметр Active отвечает за состояние сервера, активен он или не активен на данный момент.

В событие OnCreate формы не забудем добавить srv.active:=true, чтобы сервер запустился.

В параметре Port необходимо указать порт, по которому будут передаваться пакеты с информацией от клиента к серверу и обратно.

Также поместим на форму компонент Memo1 (дадим ему название log), который нам понадобиться для того, чтобы отслеживать работу чата.

Теперь у srv создадим событие onClientConnect, в котором мы сможем фиксировать подключения клиентов к серверу. В коде самого события напишем:

procedure TForm1.srvClientConnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
log.Lines.Add(‘Подключился клиент с IP адресом ‘+Socket.RemoteAddress);
end;

Таким образом мы можем узнать IP адрес подключившегося к серверу клиента.

В переменной Socket в событии хранится вся необходимая информация о клиента, который произвел подключение к серверу.

Обработчик события onClientRead служит для получения и обработки пришедшей от клиента информации. Рассмотрим пример:

procedure TForm1.srvClientRead(Sender: TObject; Socket: TCustomWinSocket);
var
str:string;
i:integer;
begin
str:=Socket.ReceiveText;
log.Lines.Add(str);
for i:= 0 to srv.Socket.ActiveConnections -1 do
srv.Socket.Connections[i].SendText(str);
end;

Переменной str мы присваиваем значение принятого текстового сообщения от клиента и добавляем принятое сообщение в лог. После этого мы отсылаем сообщение всем клиентам. Кол-во подключенных клиентов можно узнать в srv.Socket.ActiveConnections.

Предположим, если мы хотим отправить сообщение конкретному клиенту, то это можно сделать так:

srv.Socket.Connections[a].SendText(‘test message’);

В данном случае в качестве переменной a выступает числовой идентификатор в srv.Socket.Connections. Эти идентификаторы находятся в диапазоне от 0 до srv.Socket.ActiveConnections -1. Но как же, например, отправить сообщение клиенту с определенным IP адресом, который нам извествен заранее? Делается это так:

for i:= 0 to srv.Socket.ActiveConnections -1 do
if srv.Socket.Connections[i].RemoteAddress= ‘123.123.123.123’ then begin
srv.Socket.Connections[i].SendText(‘test message’);
break;
end;

Мы перебираем все соединения на сервере. Если IP адрес клиента совпадает с «123.123.123.123» (может быть и любой другой), то отправляем сообщение этому клиенту и завершаем цикл.

Ну и наконец, рассмотрим еще одно событие у TServerSocket — onClientDisconnect, которое обрабатывает отключение клиента от сервера.

procedure TForm1.srvClientDisconnect(Sender: TObject;
Socket: TCustomWinSocket);
begin
log.Lines.Add(‘Клиент ‘+socket.RemoteAddress+’ отключился от сервера.’);
end;

Теперь перейдем к клиенту. Создадим новое приложение Delphi и поместим в него компонент TClientSocket и дадим этому компоненту имя «client». Присвоим параметрам компонента client значения:

Active = false
Port = 25500
ClientType = ctNonBlocking
Address = 127.0.0.1

В параметре address укажите IP адрес компьютера, на котором запущено серверное приложение. Если провести подключение по IP адресу «127.0.0.1», то компьютер подключится грубо говоря сам к себе и будет работать с серверной программой, которая запущена там же, где и запущена клиентская. Такой способ очень удобен для тестирования сетевых приложений.

Значение Port у клиента необходимо присвоить такое же как и у сервера!

Также добавим компонент TMemo с именем log, чтобы отображать пришедшие от сервера сообщения.

Рассмотрим параметр OnCreate формы, где производиться подключение к серверу.

procedure TForm1.FormCreate(Sender: TObject);
begin
{Пытаемся установить соединение}
ClientSocket1.Open;
end;

Теперь разместим на форме клиента кнопку Button1 и однострочное текстовое поле Edit1. Они нам понадобятся для отправки сообщений на сервер. В событии OnClick у компонента Button1 напишите:

Client.Socket.SendText(Edit1.Text);

Таким образом на сервер будет отправляться сообщение, введенное в Edit1.

После того как сообщение было отослано на сервер, сервер отсылает сообщение всем клиентам, поэтому нужно принять сообщение от сервера. Для этого нам понадобиться событие OnRead:

procedure ClientRead(Sender: TObject; Socket: TCustomWinSocket);
begin
{Если пришло сообщение — добавляем его в log}
Log.Lines.Add(Socket.ReceiveText);
end;

Осталось только написать событие отключения от сервера. Для этого можем создать еще одну кнопку и в ее событии OnClick указать

Client.close;

Запустите клиент и сервер. Попробуйте отослать сообщение на сервер, сервер в ответ должен вернуть его вам. Если возникает ошибка «Asynchronous socket error 10061», то это скорее всего связано с недоступностью сервера. Проверьте соответствие портов, IP адресов и работоспособность сетевого соединения.

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

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

Размещено на 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. Применяемые таблицы и связи между ними. Пользовательский интерфейс работы с базой данных. Алгоритм работы программы "Футбольные команды и игроки". Защита от ввода неверных данных.