ГЛАВА 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.
Этот класс инкапсулирует в себе свойства и методы, обеспечивающие взаимодействие с сервером. Основные свойства объектов данного класса представлены в табл. 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, позволяет передавать не только сами данные, но и указывать их принадлежность. Рассмотрение этого языка выходит за рамки данной книги, но в примере мы используем следующие соглашения касательно формата представления передаваемых данных.
- элемент Имя клиента. Ему соответствует тег <CUSTNAME>;
- элемент Адрес клиента. Тег — <CUSTADDRESS>;
- элемент Банковские Сведения. Тег — <CUSTBANK>.
На основании этого представления нам (в смысле программирования серверной части) будет легко разобраться, какие сведения к чему относятся.
Реализуем процедуру шифрования данных и последующей их отправки (листинг 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, после чего полученные данные извлекаются, декодируются и сохраняются в базе данных.