ГЛАВА 13


Клиент-серверное взаимодействие посредством использования сокетов

Давайте присмотримся — что реально предлагают нам описанные выше средства клиент-серверного обмена данными? Браузеры автоматически формируют графическое отображение Web-страниц, посланных сервером. Для стандартного Web-сайта этого оказывается вполне достаточным, но что делать, когда нужно строить клиент-серверное взаимодействие, основанное на передаче/приеме данных, которые не являются HTML-документами? Или нужно создавать решения, в рамках которых вообще нет места браузерам, и клиентская программа сама должна формировать представление информации?

В таких случаях прибегают к сокетам. Сокеты — это объекты, которые инкапсулируют в себе все необходимые средства для обмена данными в сетях Интернет/интранет. Эти объекты сами создают соединения и посылают/принимают данные, что позволяет программисту избавиться от многих забот и организовать взаимодействие между различными программными компонентами в масштабах сетей так же легко, как, например, чтение и запись в файл. Итак, рассмотрим эти объекты подробнее.

 

Клиентский сокет

Этот объект предназначен для установления связи с сервером. Точнее, с серверным сокетом, даже если этот сокет выполнен на другом языке программирования. Нахождение нужного сервера осуществляется в сети по его имени, либо IP-адресу. После установления соединения программа может взаимодействовать с сервером, а именно — посылать и принимать данные. Интересно то, что эти данные могут быть произвольного типа. Значки компонентов TServerSocket И TClientSocket, используемые сервером и клиентом в сетевом взаимодействии, показаны на рис. 13.1.

Рис. 13.1. Размещение компонентов TServerSocket и TClientSocket

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

Таблица 13.1. Некоторые свойства объектов класса TClientSocket

Свойство

Описание

ClientType

Определяет тип клиента с позиций работы с потоками передачи/приема данных. Значение ctNonBlocking позволяет вести с сервером асинхронную работу, посредством операторов чтения/записи. Альтернативный вариант — ctBlocking устанавливает поток обмена данными с сервером, через который и ведется работа

Socket

Определяет объект типа TClientwinSocket, который и представляет программе все сокетные сервисы. Будет описан чуть позже

Active

Определяет — активно ли соединение

Address

Содержит IP-адрес сервера

Host

Содержит имя (DNS) сервера

Port

Определяет порт, на котором установлена служба, с которой требуется соединиться

Service

Определяет название службы, точнее — протокола высокого уровня (HTTP, FTP, POP), по которому будет вестись работа. Если используется собственный формат обмена данными, то поле можно оставлять пустым

Обратите внимание, что при использовании сокетов программисту предоставляются альтернативные пути идентификации сервера. Часто в одноранговых, с точки зрения топологии, локальных сетях применяются протоколы типа NetBEUI — базовый протокол производства Microsoft или IPX/SPX — сети, основанные на технологиях Novell. В этих сетях компьютеры не имеют своих IP-адресов и идентифицируются по имени компьютера. Чтобы работать в таких сетях, нужно вместо поля Address устанавливать значение host.

Если создаваемое приложение будет работать в IP-сетях, то лучше определить параметр Address по следующей причине. Даже если в сети работает служба DNS (см. гл. 1), то все равно сокетные компоненты используют IP-адреса для указания конечного получателя данных. Эти адреса ими извлекаются из запросов к DNS, сопоставляющих имени — IP-адрес. Этот процесс требует определенного времени и сопряжен с возможностью возникновения ошибок. Поэтому лучше сразу указывать IP-адрес машины-сервера. Если приложение будет развернуто в сети Ethernet, не использующей DNS, то просто необходимо указывать IP-адрес.

Перечисленные свойства устанавливают общие характеристики сокета. Основную же нагрузку при клиент-серверном взаимодействии несет на себе объект, указанный в параметре socket. Наиболее часто используемые методы объектов класса iciientsocket указаны в табл. 13.2.

Таблица 13.2. Некоторые методы объектов класса Tdientsocket

Метод

Описание

Open

Открывает соединение

Close

Закрывает соединение

Как видим, эти методы не предоставляют прямого интерфейса для отправки или получения данных, они находятся все в том же объекте socket. События объектов класса Tdientsocket описаны в табл. 13.3.

Таблица 13.3. События объектов класса Tdientsocket

Событие

Описание

OnConnect

Возникает, когда установлено соединение с сервером

OnConnecting

Условием возникновения этого события является попытка установления соединения с сервером

OnDisconnect

Возникает при отсоединении от сервера

OnError

Как следует из названия, причиной появления этого события является возникновение ошибки

OnLookup

Означает начало процесса сопоставления имени сервера через службу DNS

OnRead

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

OnWrite

Означает готовность сокета к приему данных, которые будут вслед за тем отправлены серверу i

Как видим, данный объект инкапсулирует только свойства и методы касательные соединения. Для непосредственной работы с данными служит объект класса TCiientwinSocket, доступ к которому может осуществляться посредством Свойства Socket объекта ClientSocket.

 

Класс TClientwinSocket

Этот класс инкапсулирует в себе свойства и методы, обеспечивающие взаимодействие с сервером. Основные свойства объектов данного класса представлены в табл. 13.4.

Таблица 13.4. Некоторые свойства объектов класса TCiientwinSocket

Свойство

Описание

ClientType

Свойство аналогично соответствующему свойству объекта класса TClientSocket

Addr

Содержащая полное описание данного сокета (адрес, имя в DNS, протокол, порт)

Connected

Булевское выражение, отображающее состояние — подключен ли данный сокет к серверу?

LocalAddress

Содержит IP-адрес клиента

LocalHost

Содержит имя компьютера клиента в сети

LocalPort

Указывает номер сетевого порта, который используется данным сокетом

LookupState

Содержит идентификатор состояния сокета в период открытия соединения. Принимает три значения: Isldle, IsLookupAddress и IsLookupService. Первое значение показывает отсутствие какой-либо деятельности. Второе — поиск IP-адреса через доменное имя, используя службу DNS. Последнее — установление типа службы (протокола высокого уровня), с которой будет вестись работа

RemoteAddr

. Структура, аналогичная Addr, но относящаяся к серверу

RemoteAddress

Содержит IP-адрес сервера

RemoteHost

Содержит имя сервера

RemotePort

Указывает номер порта сервера

Как видно из табл. 13.4, свойства объектов класса TCiientwinSocket более детально описывают состояние сокета, что позволяет программе анализировать его и определять свои дальнейшие действия. Методы таких объектов описаны в табл. 13.5.

Таблица 13.5. Некоторые методы объектов класса TCiientwinSocket

Метод

Описание

Close

Закрывает текущее соединение

LookupName

Извлекает из URL часть, являющуюся именем ресурса и заполняет соответствующее поле записи, описывающей удаленный или локальный ресурс (TlnAddr)

LookupService

То же, что и LookupName, только касательно названия службы (протокола высокого уровня)

Open

Открывает соединение

ReceiveBuf

Считывает в буфер информацию, пришедшую на сокет с сервера

ReceiveLength

Содержит число бит (символов), которые получены соке-том, и могут быть прочитаны

ReceiveText

Возвращает строку, содержащую содержимое, полученное сокутом

SendBuf

Отправляет содержимое буфера, указанного в параметре вызова метода серверу

SendStream

Перенаправляет поток, указанный в качестве параметра, серверу

SendStreamThenDrop

Действие аналогично предыдущему с закрытием соединения после завершения передачи

SendText

Отправляет содержимое строки, указанной в качестве параметра, серверу

Данный объект предоставляет все возможности для отправки/приема данных при использовании типа ctNonBiocking сокета. Как видим, здесь есть два сорта операторов чтения и записи, работающие с буферами или строковыми переменными. Кроме того, присутствуют эквивалентные методы для отправки данных. В случае, когда нужно считать данные в поток или отправить содержимое потока серверу, используется тип последнего TWinSocketStream, который создается на основе объекта ClientWinSocket. Методы объектов класса TWinSocketStream представлены в табл. 13.6.

Таблица 13.6. Некоторые методы объектов класса TWinSocketStream

Метод

Описание

Create

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

Destroy

Уничтожает существующий поток

Read

Считывает данные из потока в буфер

WaitForData

Переходит в режим ожидания готовности сокета к отправке/приему данных

Write

Записывает данные из буфера в поток

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

При создании приложения, которое будет взаимодействовать с сервером посредством сокетов, прежде всего,, нужно внести данные о сервере в свойства объекта clientsocket. После этого, через свойство socket можно получить доступ к соответствующему объекту clientwinsocket. Если используется асинхронная передача информации, т. е. клиент имеет тип ctNonBiocking, то посредством вызова необходимых методов можно принимать/отправлять данные клиенту. В противном случае, с данным сокетом необходимо сопоставить поток, который и будет осуществлять прием/передачу данных. При использовании этого типа клиента события onRead и onWrite, разумеется, не имеют никакого смысла, и поэтому не возникают.

 

Пример реализации сокетного клиента

Вернемся к примеру, который мы начали реализовывать в предыдущей главе. А именно, речь шла об ActiveX-форме, которая должна отображать данные заказа, предоставлять возможность для распечатки счета и отправлять на сервер реквизиты покупателя, с целью последующего их сохранения в базе данных фирмы. В этом случае, уже неважно, что речь идет об ActiveX-элементах управления. Если бы форма не внедрялась в Web-страницу, то работа с сокетами была бы полностью аналогична.

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

Поместим в проект компонент clientsocket. После этого нужно установить его параметры. Адрес сервера, установленного на локальную машину, как отмечалось выше 127.0.0.1, имя хоста — localhost или имя компьютера в сети. Наше приложение, работая на стороне клиента, лишь однажды отправит данные через сокет, поэтому наиболее простым вариантом его реализации является применение метода sendText, без использования потоков в программе. Исходя из этого, тип сокета установим в ctNonBiocking. Обычно, Web-сервер имеет номер сетевого порта равный 80. Это значит, что наша серверная программа должна иметь другой сетевой порт, например — 800. Главное, чтобы при назначении номера порта не возникло конфликтов с другими сетевыми службами из-за одинаковых номеров. Таким образом, присвоим свойству Port значение 800.

После инициации соединения с сервером, например, путем установки свойства Active в True, сокет автоматически ищет соответствующий ресурс, и после его нахождения проверяет готовность серверного сокета к приему данных. Как только такое уведомление получено (на низком сетевом уровне), сокет инициирует событие ciientsocketwrite. Как раз в соответствующую данному событию процедуру мы и поместим код, отсылающий содержимое заполненных полей на сервер.

На самом деле, можно по-разному закодировать данные. Все зависит от вкуса и конкретных привычек программиста. Однако существует тенденция к усилению роли стандартного использования данных путем их представления в формате extensible Markup Language (в дальнейшем — XML).

Этот язык представляет собой набор базовых требований к реализациям собственных средств разметки с целью унификации документооборота и обмена данными. Поясню вышесказанное. Предположим, что наша фирма "Два кирпича" имеет пять региональных представительств. Каждое из этих представительств, в свою очередь продает получаемые материалы, через свои Web-сайты. Более того, несколько раз в день головная фирма рассылает представительствам информацию об ассортименте и ценах на товары в виде прайс-листов. Разумеется, все изменения должны автоматически вноситься в базу данных фирмы и содержимое сайта. Ручное внесение изменений недопустимо в связи с необходимостью постоянного контроля над целостностью данных, что при большом количестве позиций крайне затруднительно. Следует искать автоматизированные решения. Для обеспечения простого взаимодействия с информационными системами конкретных представительств необходимо иметь стандарт представления данных. В нашем случае, с одной стороны, информационная система должна анализировать содержимое документа с целью выявления нужных данных, которые будут внесены в БД фирм, а с другой стороны, документ, составленный по этим правилам, должен иметь более широкие возможности для визуального представления данных, нежели простая таблица.

Понятно, что возможности HTML достаточно скудно реализуют выдвинутые требования, поскольку отражают лишь форматирование данных. Невозможно прямо указать тип передаваемых данных, назначить им какие-либо специальные атрибуты. Использование форматирования, базирующегося на языке XML, позволяет передавать не только сами данные, но и указывать их принадлежность. Рассмотрение этого языка выходит за рамки данной книги, но в примере мы используем следующие соглашения касательно формата представления передаваемых данных.

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

Реализуем процедуру шифрования данных и последующей их отправки (листинг 13.1). Объект клиентский сокет назовем cisock.

Листинг 13.1. Реализация процедуры шифрования и отправки данных через сокетное соединение примера 

procedure Tshop.ClSockWrite(Sender: TObject; Socket: TCustomWinSocket);

 begin Content:='<CUSTNAME>'+custname.text+ '</CUSTNAMEXCUSTADDRESS>'+ custaddress.Text+'</CUSTADDRESSXCUSTBANK>' +custbank.Text+'</CUSTBANK>'; Cisock.Socket.SendText(content);

  end;

Чтобы использовать шаблон для введения кода процедуры, вызываемой как отклик на событие, нужно сделать двойной щелчок мышью в названии процедуры в окне Object Inspector напротив соответствующего события. В данном случае, это событие OnWrite.

Как видим, процесс обмена данными через сокеты предельно прост, несмотря на то, что его реализация на низком уровне представляет собой очень содержательную задачу. Вот как проявляются фантастические возможности RAD-среды Delphi!

Теперь осталось только вписать одну строчку в процедуру Tshop.printbtnciick, а именно

clsock.active:=true

Как только пользователь нажмет на кнопку Печать, сокет произведет попытку соединения с сервером. В случае если соединение случится, то пойдет выполнение процедуры cisockwrite. В противном случае, возникнет исключительная ситуация. При разработке реальных проектов нужно всегда помещать вызов метода Open в конструкцию, обеспечивающую корректную работу с исключительными ситуациями try ... except.

 

Серверный сокет

Серверный сокет предполагает обмен данными с клиентами. В принципе, от клиентской реализации он отличается тем, что способен работать с несколькими клиентами одновременно. На базе серверного сокета можно создавать программы — серверы, которые предоставляют возможность внедрять собственные протоколы верхнего уровня, т. е. реализовывать различные схемы обмена данными. Свойства объектов класса TServerSocket представлены в табл. 13.7.

Таблица 13.7. Основные свойства объектов класса TServerSocket

Свойство

Описание

Socket

Представляет собой ссылку на объект типа TServerWinSocket, который и предоставляет основные сервисы

Server Type

Определяет тип сервера с позиций работы с потоками передачи/приема данных. Значение stNonBlocking позволяет вести асинхронную работу с сервером посредством операторов чтения/записи. Альтернативный вариант— значение stThreacffllocking устанавливает поток обмена данными с сервером, через который и ведется работа

ThreadCacheSize

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

Active

Определяет — активен ли сокет

Port

Содержит номер сетевого порта, на который установлена служба, работу которой обеспечивает данный сокет

Service

Содержит название службы, точнее — протокола высокого уровня (HTTP, FTP, POP), по которому будет вестись работа. Если используется собственный формат обмена данными, поле можно оставлять пустым

Аналогично клиентскому сокету, в сетевом сокете основную функциональную нагрузку несет на себе объект, указанный в поле socket, который будет описан чуть позже. Наиболее часто используемые методы объектов класса

TServerSocket описаны табл. 13.8.

Таблица 13.8. Некоторые методы объектов класса TServerSocket

Метод

Описание

Open

Включает сокет

Close

Отключает сокет

Аналогично клиентскому сокету, вызов метода open устанавливает значения поля Active в true, а вызов close — в false. События, которые могут возникать у объектов класса TServerSocket, приведены в табл. 13.9.

Таблица 13.9. События объектов класса TServerSocket

Событие

Описание

OnClientConnect

Возникает после завершения процесса соединения с клиентом

OnCl lent Disconnect

Возникает после рассоединения с клиентом

OnClientError

Возникает при наличии ошибки

OnClientRead

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

OnClientWrite

Идентифицирует состояние, когда клиентский сокет ждет передачи данных от серверного

OnGetSocket

Возникает, когда установлено соединение с клиентом, и этому соединению можно назначить свой экземпляр объекта, реализующий основные возможности работы с клиентом — ServerClientWinSocket. Если программист хочет реализовать специфичные задачи, то он должен написать процедуру, возвращающую конкретный объект класса TServerClientWinSocket, который станет обслуживать запросы конкретного клиента (в результате соединения с которым и возникло это событие)

OnGetThread

Возникает, когда можно назначать конкретный поток сетевому соединению

OnThreadEnd

Возникает, когда соединение с клиентом завершено и соответствующий ему поток уничтожается

OnThreadStart

Возникает в связи с инициализацией потока, ассоциированного с данным сетевым соединением

OnAccept

Возникает, как только подтверждено создание соединения с клиентом

OnListen

Возникает непосредственно перед началом процесса последовательных опросов порта на предмет новых клиентских соединений

Как и в случае с клиентским сокетом, для работы с сокетными подключениями создается объект, доступный посредством свойства Socket. Необходимо учитывать, что серверный сокет может обслуживать несколько подключений одновременно, и поэтому для каждого подключения существует свой экземпляр объекта, предоставляющий сетевые сервисы. Графически последовательность выбора свойств объектов для доступа к непосредственному соединению изображена на рис. 13.2.

Рис. 13.2. Алгоритм обращения к полям объектов для доступа непосредственно к соединению

Обратите внимание, что, как и в случае клиентского сокета, объект ServerSocket служит для установления и обслуживания общих свойств соединения, но в нем свойство socket указывает не на конечное соединение, а на другой объект — serverwinsocket, который в свою очередь осуществляет менеджмент соединений. Свойства последнего сведены в табл. 13.10.

Таблица 13.10. Некоторые свойства объекта serverNinSocket

Свойство

Описание

ActiveConnections

Содержит число текущих соединений

ActiveThreads

Содержит число запущенных потоков, посредством которых и происходит обмен данными

Connections

Возвращает число текущих соединений с клиентами

IdleThreads

Содержит число потоков, хранящихся в кэше сокета

ServerType

Определяет тип сервера с позиций работы с потоками передачи/приема данных. Значение stNonBlocking позволяет вести с сервером асинхронную работу посредством операторов чтения/записи. Альтернативный вариант— stBlocking устанавливает поток обмена данными с сервером, через который и ведется работа

ThreadCacheSize

Содержит число потоков, которые кэшируются в памяти

Addr

Содержит полное описание данного сокета (адрес, имя в DNS, протокол, порт)

Connected 

Массив объектов класса TCustomWinSocket, предоставляющий доступ к методам и свойствам этих объектов. Каждому соединению соответствует свой индекс массива, в порядке подключения

Handle

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

LocalAddress

Содержит IP-адрес сервера

LocalHost

Содержит имя сервера в DNS

LocalPort

Указывает номер порта, на котором размещена эта сетевая служба

RemoteAddr

Содержит IP-адрес клиента

RemoteHost

Содержит имя клиента в DNS

ReraotePort

Указывает номер сетевого порта, используемого программой, с которой ведется взаимодействие

SocketHandle

Содержит Handle, присвоенное данному сокету

Handle окна, или другого объекта, это целое число, которое им присваивается операционной системой Windows. При обычном создании окон с помощью Delphi программисту незачем использовать этот параметр, но если окна создаются путем прямого вызова API-функций или модернизируются ими, к примеру, делаются полупрозрачными или овальными, то параметр Handle используется повсеместно. Кроме того, если вы хотите работать напрямую с системой Windows, окнами и процессами другого приложения, то индексация нужных объектов также осуществляется путем использования этих параметров. Методы объекта serverwinSocket сведены в табл. 13.11.

Таблица 13.11. Некоторые методы объекта ServerwinSocket

Метод

Описание

Disconnect

Закрывает то сокетное соединение, Handle которого указан в параметре

Listen

Инициирует процесс опроса порта на предмет получения запроса на сетевое соединение

Close LookupName

Закрывает текущее соединение

Извлекает из URL часть, являющуюся именем ресурса, и заполняет соответствующее поле записи, описывающей удаленный или локальный ресурс (ilnAddr)

LookupService

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

Open

Открывает соединение

ReceiveBuf

Считывает информацию, пришедшую на сокет с сервера в буфер

ReceiveLength

Содержит число бит (символов), которые получены сокетом, и их нужно считать

ReceiveText

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

SendBuf

Отправляет содержимое буфера, указанного в параметре, серверу

SendStream

Перенаправляет поток, указанный в качестве параметра, серверу

SendStreamThenDrop

Действие аналогично предыдущему с закрытием соединения после завершения передачи

SendText

Отправляет содержимое строки, указанной в качестве параметра, серверу

Легко заметить, что основные методы, предназначенные для приема/отправки данных, одинаковы у объектов класса TServerwinSocket и TClientwinSocket. Связано это с тем, что они оба являются наследниками класса TCustonwinSocket. Естественно, что и работа с данными методами объектов указанных классов одинакова. Поэтому, чтобы узнать, как читать и отправлять данные, используя серверный сокет, загляните в соответствующий раздел описания клиентского сокета.

События объектов класса TServerwinSocket и TServerSocket практически одинаковы, поэтому на них мы подробно останавливаться не будем.

 

Пример реализации сонетного сервера

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

Мы можем создать сокетный сервер, используя обычные визуальные средства Delphi. Это освободит нас от обработки множества событий. По сути, серверные приложения, базирующиеся на сокетах, создаются точно так же, как и клиентские. Отличие заключается в том, что после установки всех свойств и активизации объекта клиентский сокет пытается установить соединение с сервером, а серверный опрашивает сетевой порт. Как бы там ни было, все, что нужно сделать в примере, — это поместить объект в форму, установить его свойства и активизировать. Только нужно поместить обработчик события OnClientRead.

Давайте создадим новый проект под названием appserv. В форму следует поместить кнопку Button, компонент servetsocket, свойству name которого придадим значение SrvrSock, port — 800, host — localhost. При нажатии на кнопку этот объект должен активизироваться. Присвоим надписи на кнопке (поле caption) значение Старт! Для активизации серверного модуля в процедуре Buttoniciick (шаблон которой вызывается, как обычно, путем двойного щелчка мышью по этой кнопке в режиме проектирования) следует вписать строчку.

SrvrSock.active:=true;

Поскольку наш проект должен добавлять полученные данные в БД в форме SQL-команды, то на форму следует добавить объект Query. Присвоим ему это же имя. База данных, по-прежнему, должна называться shop.

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

Листинг 13.2. Реализация обработчика события OnClientRead 

procedure TForml. SrvrSockClientRead(Sender: TObject," Socket: TCustomWinSocket); type Custinfo = record name: string; address: string;

 bank: string;

end;

function ExtractXMLcontent(s, constr: string): string; begin Result:=Copy(s,(Pos('<'+constr+'>',s)+Length(constr}+2),

(Pos('</'+constr+'>',s)-(Pos(I<'+constr+I>',s) +Length(constr)+2)));

end; var content: string;

currcust: custinfo; numcust:integer; 

begin

content:=Socket.ReceiveText;

currcust.name:=ExtractXMLcontent(content,'CUSTNAME');

currcust.address:=ExtractXMLcontent(content,'CUSTADDRESS');

currcust.bank:=ExtractXMLcontent(content,'CUSTBANK');

Query.close;

Query.sql.Clear;

Query.sql.Add('SELECT ID FROM klients');

query.open;

query.Last;

numcust:=strtoint(query.FieldValues['ID'] ) +1;

Query.close;

Query.SQL.Clear;

Query.sql.Add('INSERT INTO klients(ID, KlientsName, KlientsAddress, KlientsBank) Values ('+inttostr(numcust) + ',"'+currcust. name+'", "'+currcust.address+'", "r+currcust.bank+'"}');

Query.ExecSQL;

  end;

Эта процедура содержит функцию ExtractXMLcontent, возвращающую значение данных, занесенных в теги, передаваемые в функцию с переменной constr.

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

Если нам потребовалось бы одновременно обслуживать несколько подключений, то строка

content:=Socket.ReceiveText;

перешла бы в

content:=Socket.connections[i].ReceiveText;

где i — номер соединения, с которым ведется работа.

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