Delphi и удаленный доступ к COM-серверам с помощью протокола TCP/IP

Сергей Трепалин, Наталия Елманова

Удаленный доступ к COM-серверам

Применение протокола TCP/IP  для удаленного доступа к COM-серверам

Borland Socket Server

Применение компонента TSocketConnection

Безопасность передачи данных при работе с TSocketConnection

 

В нашем журнале мы уже неоднократно рассказывали о создании серверов и контроллеров автоматизации и об их применении в случае, когда сервер и контроллер выполняются на одном и том же компьютере. Однако это не единственный способ применения автоматизации — нередко сервер и контроллер выполняются на разных компьютерах. Ниже мы выясним, каковы способы реализации подобного удаленного доступа к серверам автоматизации, и рассмотрим один из них, а именно удаленный доступ с помощью протокола TCP/IP.

Удаленный доступ к COM-серверам

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

Отметим, что и внутрипроцессный, и внепроцессный COM-сервер можно превратить в удаленный (то есть запускаемый на компьютере, отличном от компьютера, содержащего контроллер; для использования  внутрипроцессного сервера в качестве удаленного требуется какое-либо входящее в состав Windows 2000 приложение, в адресное пространство которого следует загрузить этот сервер, например dllhost.exe). Такой способ использования серверов особенно удобен в случае, когда сервер требует для своей работы наличия особых ресурсов, недоступных на всех компьютерах, содержащих контроллеры, таких как дополнительный объем оперативной памяти, уникальное оборудование, дополнительное программное обеспечение, требующее сложной конфигурации, настройки и поддержки. Нередко удаленный доступ применяется и в тех ситуациях, когда это выгодно со стороны схемы лицензирования сервера, например если лицензия на использование сервера или каких-либо применяемых им сервисов требуется для каждого компьютера, на котором он установлен, но при этом в лицензионном соглашении не указаны ограничения на число клиентов, подключаемых к такому серверу (типичный пример подобного лицензирования — лицензирование приложений Microsoft Office), либо когда стоимость серверной лицензии существенно превышает стоимость клиентской лицензии.

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

Другой пример использования удаленного запуска серверов автоматизации, вызывающий в последнее время немалый интерес разработчиков информационных систем, — вариант трехзвенной информационной системы с тонким клиентом — контроллером автоматизации и с сервером автоматизации (в данном случае он называется сервером доступа к данным), предоставляющим тонкому клиенту сервисы, связанные с доступом к данным, содержащимся, в свою очередь, в определенной серверной СУБД. В этой ситуации, помимо экономии ресурсов компьютеров, содержащих контроллеры, имеется еще одно преимущество. Оно состоит в том, что для доступа к данным используются программное обеспечение, требующее отдельной установки, сложной настройки и поддержки, такое как клиентская часть серверной СУБД, а также библиотеки, реализующие универсальные механизмы доступа к данным. Если это программное обеспечение используется только сервером, но не используется контроллерами, то сопровождение такого распределенного приложения оказывается менее затратным, нежели сопровождение стандартного приложения в архитектуре «клиент-сервер».

При рассмотрении работы указанных серверов возникает естественный вопрос: каким образом контроллер обращается к свойствам и методам объектов, расположенных за пределами его адресного пространства? Для этого вспомним, как вообще взаимодействуют COM-клиент и COM-сервер.

При обращении COM-клиента к COM-серверу происходит поиск его местоположения в реестре Windows. Если сервер является внутренним (то есть выполненным в виде DLL), то эта DLL загружается в адресное пространство клиента. Если же сервер локальный, используется функция CreateProcess, которая   загружает исполняемый файл сервера  в отдельное адресное пространство. При этом  в адресных пространствах клиента и сервера создаются прокси и стаб и используется маршалинг — передача данных между этими объектами с помощью их «упаковки» в пакеты посредством функции CoMarshalInterface и «распаковки» посредством CoUnMarshalInterface. Именно таким образом совместно работают локальный сервер автоматизации и его контроллер, а поскольку их взаимодействие заключается в загрузке сервера в оперативную память того же самого компьютера, на котором выполняется контроллер, то никаких особых противоречий относительно безопасности данных не возникает.

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

Как можно технически реализовать подобное разрешение? В принципе имеется два способа. Первый заключается в применении сервисов DCOM (Distributed Component Object Model), и тогда вся работа по поиску и удаленному запуску приложения возлагается на сервисы операционной системы. Недостатками  этого способа удаленного запуска серверов автоматизации является то, что компьютер, на котором запускается сервер, должен иметь доступ к спискам  всех пользователей сети и что сервер автоматизации необходимо зарегистрировать в реестре клиента.

Второй способ состоит в применении универсального COM-клиента, который, с одной стороны, способен запускать любой сервер вообще (или из какой-то определенной категории серверов) и вызывать любой его метод, а с другой — может взаимодействовать с контроллером с помощью какого-то сетевого протокола, например TCP/IP. Ниже мы подробно рассмотрим данный способ.

В начало В начало

Применение протокола TCP/IP  для удаленного доступа к COM-серверам

Удаленный доступ к COM-серверам  с помощью протокола TCP/IP обычно реализуется путем создания COM-клиента, который располагается на том же компьютере, что и сервер, и может инициировать его запуск и вызывать его методы. Удаленный контроллер автоматизации при этом  обращается не к серверу, а к COM-клиенту, передавая ему  имя сервера, имена методов и их параметры и получая в ответ результаты их выполнения.  Таким образом, никакие сервисы DCOM в этом случае не используются — COM-клиент и COM-сервер выполняются  на одном и том же компьютере.

Реализаций подобных клиентов может быть довольно много. Их можно создавать самим, а можно применять готовые. Одной из первых таких реализаций (ныне ставших уже историей) был продукт OLEnterprise, входивший в комплект поставки Delphi 3 Client/Server и Delphi 4 Enterprise. Утилита Object Factory, входящая в состав этого продукта,  представляла собой универсальный COM-клиент, который всем пользователям, имеющим установленную клиентскую часть OLEnterprise, предоставлял доступ к  фиксированному набору COM-серверов. Для того чтобы COM-сервер был доступен для удаленного запуска, требовалось наличие специальных ключей в реестре сервера и клиента, и для их создания в состав OLEnterprise входила специальная утилита Object Explorer, используемая и на компьютере, содержащем COM-сервер, и на компьютерах, содержащих клиентские приложения.

В начало В начало

Borland Socket Server

В состав всех версий Delphi начиная с 3.01 входит другой универсальный COM-клиент — Borland Socket Server (файл scktsrvr.exe в каталоге Delphi\Bin).  Версии этого приложения, входившие в состав Delphi 3 и Delphi 4, при запуске на каком-либо компьютере позволяли осуществить доступ к любым COM-серверам, причем со всех удаленных компьютеров, которые могли обращаться к данному компьютеру с помощью протокола TCP/IP (в общем случае не только посредством локальной сети, но и через Интернет). При этом, естественно, к компьютеру, содержащему клиентское приложение, не предъявлялось практически никаких требований, кроме собственно поддержки протокола TCP/IP и возможности доступа к компьютеру, содержащему Socket Server, по указанному порту.  Очевидно, что подобное приложение, будучи запущенным на компьютере, представляло собой серьезную угрозу безопасности данных. Поскольку с его помощью любой удаленный пользователь мог инициировать запуск и выполнение любого метода любого имеющегося на данном компьютере COM-сервера, то пользоваться им следовало очень осторожно.

Версии Borland Socket Server, входящие в состав Delphi 5 и Delphi 6, были слегка усовершенствованы. Эти приложения можно запускать в двух режимах — с предоставлением доступа ко всем COM-серверам (как в прежних версиях Socket Server) и с предоставлением доступа к ограниченному набору серверов, специальным образом зарегистрированных в реестре.

Загрузить  Socket Server можно как исполняемый файл, просто запустив исполняемый файл scktsrvr.exe из каталога Delphi\Bin, или зарегистрировать его как сервис Windows NT или Windows 2000  (с помощью команды scktsrvr.exe /install). После запуска в панели задач появится соответствующая пиктограмма, при щелчке по которой появляется окно Socket Server (рис. 1).

С помощью окна этого приложения можно указывать номера портов, по которым можно вести обмен данными с удаленными клиентами, а также управлять доступом к COM-серверам. Режим доступа можно установить, выбрав или отключив опцию Connections | Registered Objects Only. При изменении значения этой опции Socket Server следует перезапустить.

Borland Socket Server не требует ни клиентских частей, ни дополнительных настроек на рабочих станциях, на которых предполагается использовать контроллер, и не делает никаких предположений относительно них (кроме, естественно, того, что данная рабочая станция оснащена 32-разрядной версией Windows). Как универсальный COM-клиент, он и подобные ему приложения идеальны при осуществлении удаленного доступа к серверам автоматизации через Интернет или с использованием технологий,  применяемых в Интернете, — в этом случае очень важно избегать всех действий, связанных с установкой и конфигурацией дополнительного программного обеспечения  на компьютеры, которые могут содержать контроллеры (естественно, установки самих контроллеров при этом избежать не удастся, но они могут быть выполнены  в виде элементов управления ActiveX или в виде дистрибутивов, устанавливающихся с Web-страницы, что практически решает проблемы их поставки и конфигурации).

В начало В начало

Применение компонента TSocketConnection

Создать клиентское приложение, использующее доступ с помощью протокола TCP/IP, несложно. Для этой цели обычно применяется компонент TSocketConnection. Проиллюстрируем его применение на элементарном примере, создав простейший контроллер автоматизации Microsoft Excel. Для этого создадим новый проект и поместим на его главную форму кнопку и компонент TSocketConnection. Установим его свойство Address равным IP-адресу компьютера, на котором запущен Borland Socket Server (можно вместо этого указать имя данного компьютера в качестве значения свойства Host).  Установим свойство ServerName компонента SocketConnection1 равным программному идентификатору (ProgID) сервера автоматизации  — Excel.Application.  Если сервер зарегистрирован на данном компьютере и ProgID указан правильно, то в свойстве ServerGUID появится GUID данного COM-сервера. Чтение GUID будет производиться из реестра компьютера, на котором запущен Socket Server, так как именно Socket Server и возвращает этот GUID.

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

procedure TForm1.Button1Click(Sender: TObject);  
var  
  App : Variant;  
begin  
  SocketConnection1.Connected := True;  
  App := SocketConnection1.AppServer;  
  App.Visible := True;  
  App.Workbooks.Add;  
//и так далее...  
end;  

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

В данном примере мы использовали  режим работы Socket Server, позволяющий обращаться к произвольным серверам автоматизации. Если же вернуться к режиму работы, установленному  по умолчанию, мы сможем с помощью этого универсального клиента обращаться только к серверам, особым образом зарегистрированным. Таковыми могут быть DataSnap-серверы, при регистрации которых создаются определенные ключи реестра, на основании которых Socket Server предоставляет к ним доступ. При создании COM-объектов, применяемых в таких серверах, с помощью предназначенных для этого экспертов, к их методу  UpdateRegistry автоматически добавляется процедура EnableSocketTransport, создающая в реестре ключи, с помощью которых Socket Server  проверяет, могут ли клиенты обращаться к данному серверу с помощью этого протокола. Эти ключи могут быть удалены процедурой  DisableSocketTransport. Удалив соответствующие вызовы из процедуры UpdateRegistry, мы можем запретить доступ к данному DataSnap-серверу c помощью протокола TCP/IP, но при этом данный сервер может быть доступен с помощью других протоколов.

Начиная с Delphi 5 компонент TSocketConnection обладает свойством SupportCallbacks. Если его значение равно True, клиентское приложение принимает нотификационные сообщения от сервера, которые очень важны при многопользовательской работе, например когда нужно уведомлять клиентов об изменениях, внесенных в общие данные другими клиентами.  Отметим, что в случае применения компонента TSocketConnection  при значении его свойства  SupportCallback равным True следует иметь установленную библиотеку Winsock2, доступную во всех 32-разрядных версиях Windows, кроме Windows 95. Для Windows 95 эту библиотеку можно найти на Web-сайте Microsoft: http://www.microsoft.com/windows95/downloads/.

Если же мы не используем callback-функции этого компонента, можно просто установить это свойство равным False, что позволяет сделать приложение не зависящим от наличия Winsock2.

В начало В начало

Безопасность передачи данных при работе с TSocketConnection

Один из аргументов против использования компонента TSocketConnection состоит в том, что в случае обычного использования этого компонента отсутствует безопасность при передаче данных. Действительно, можно легко получить доступ к данным, передаваемым с помощью протокола  TCP/IP. Формат данных также легко может быть расшифрован, так как модуль SConnect.pas, который входит в комплект поставки Delphi, содержит открытый код класса TDataBlockInterpreter, в котором происходит формирование данных для компонента TClientDataSet, используемого в ряде DataSnap-серверов.

Однако в Delphi предусмотрена возможность защиты передаваемых пакетов данных от несанкционированного доступа. Механизм защиты очень прост: создается COM-сервер, который должен поддерживать интерфейс IDataIntercept. Далее CLSID фабрики классов этого COM-сервера указывается в свойстве TSocketConnection.InterceptGUID клиентского приложения, и он же  должен быть указан в элементе управления InterceptGUID приложения Scktsrvr.exe, запущенного на сервере. Интерфейс IDataIntercept объявлен в модуле SConnect.pas так:

IDataIntercept = interface  
  ['{B249776B-E429-11D1-AAA4-00C04FA35CFA}']  
  procedure DataIn(const Data: IDataBlock); stdcall;  
  procedure DataOut(const Data: IDataBlock); stdcall;  
end;  

Перед отправкой пакета клиенту (или серверу) серверное (или соответственно клиентское) приложение обращается к COM-серверу, получает ссылку на интерфейс IDataIntercept и вызывает метод DataOut. Сразу после  получения пакета вызывается метод DataIn. В качестве параметров обоих методов фигурирует ссылка на интерфейс IDataBlock, который определен в модуле SConnect.pas следующим образом:

IDataBlock = interface(IUnknown)  
  ['{CA6564C2-4683-11D1-88D4-00A0248E5091}']  
    function GetBytesReserved: Integer; stdcall;  
    function GetMemory: Pointer; stdcall;  
    function GetSize: Integer; stdcall;  
    procedure SetSize(Value: Integer); stdcall;  
    function GetStream: TStream; stdcall;  
    function GetSignature: Integer; stdcall;  
    procedure SetSignature(Value: Integer); stdcall;  
    procedure Clear; stdcall;  
    function Write(const Buffer; Count: Integer): Integer;  
      stdcall;  
    function Read(var Buffer; Count: Integer): Integer;   
      stdcall;  
    procedure IgnoreStream; stdcall;  
    function InitData(Data: Pointer; DataLen: Integer;  
      CheckLen: Boolean): Integer; stdcall;  
    property BytesReserved: Integer read GetBytesReserved;  
    property Memory: Pointer read GetMemory;  
    property Signature: Integer read GetSignature   
      write SetSignature;  
    property Size: Integer read GetSize write SetSize;  
    property Stream: TStream read GetStream;  
  end;  

Интерфейс IDataBlock  обеспечивает доступ к ОЗУ (где хранится исходный пакет данных) и содержит ряд методов, позволяющих модифицировать эти данные. Поэтому идеология защиты данных от несанкционированного доступа заключается в следующем: перед отправкой пакета данные кодируются в методе DataOut, а после получения такого пакета в методе DataIn  данные декодируются. Степень защиты данных определяется выбранным алгоритмом шифрования и может быть очень высокой.

Создадим COM-сервер, реализующий защиту данных, и оформим его в виде DLL. Для этого сначала выполним команду File | New | Other, выберем закладку ActiveX и в ней вызовем эксперт ActiveX Library. Сохраним проект под именем DataIntr. Далее, при открытом проекте, вновь обратимся к закладке ActiveX и вызовем эксперт COM Object. Этот эксперт к тому же позволяет создать COM-сервер, который не поддерживает библиотеки типов и не поддерживает интерфейсов автоматизации. В появившемся диалоге снимем отметки Mark Interface Oleautomation и Include Type Library. Дадим вновь созданному классу имя  DataInt (рис. 2).

После нажатия кнопки OK в полученном модуле прежде всего сошлемся на модуль SConnect в директиве Uses. Далее вручную в определении класса укажем имя поддерживаемого интерфейса:

type  
  TDataInt = class(TComObject, IDataIntercept)  

Два метода этого интерфейса — DataIn и DataOut — следует объявить в секции protected класса TDataInt. Для этого проще всего скопировать их заголовки из файла SConnect.pas.

Теперь следует реализовать оба этих метода. С этой целью вначале необходимо получить доступ к пакету. IDataBlock имеет два свойства для доступа к памяти: указатель Memory и поток Stream. Воспользуемся свойством Stream для доступа к данным. Однако целиком весь поток данных считывать и перекодировать нельзя, так как первые 8 байт этого потока являются зарезервированными: четыре байта отводятся для цифровой подписи — по ее значению компоненты, ответственные за транспорт данных (TSocketConnection, TDataSetProvider и др.), определяют, получили ли они данные в доступном формате, а в следующих четырех байтах хранится размер пакета — если при перекодировке размер пакета изменяется, то новый размер необходимо сохранять в этой переменной. Реализация кода метода DataIn выглядит следующим образом:

procedure TDataInt.DataIn(const Data: IDataBlock);  
var  
  S:TStream;  
  TempStream:TMemoryStream;  
  N,I:integer;  
  B:byte;  
begin  
  TempStream:=nil;  
  try  
    TempStream:=TMemoryStream.Create;  
    N:=Data.Size;  
    TempStream.Size:=N;  
    S:=Data.Stream;  
    S.Seek(Data.BytesReserved,soFromBeginning);  
    S.Read(TempStream.Memory^,N);  
    //Now all data content is inserted into TempStream;  
    Data.Clear;  
    TempStream.Seek(0,soFromBeginning);  
    for I:=1 to N do begin  
      TempStream.Read(B,sizeof(B));  
      B:=not B;  
      Data.Write(B,sizeof(B));  
    end;  
    Data.Stream;  
  finally  
    TempStream.Free;  
  end;  
end;   

Сначала создается временный поток TempStream, куда копируется пакет данных. Для копирования пакета запоминаем ссылку S в потоке и начало потока смещаем на 8 байт вызовом метода Seek. Поскольку рассмотрение алгоритмов перекодировки выходит за рамки данной книги, воспользуемся простейшим: инвертируем все биты в пакете данных. Инверсию можно было бы выполнить и без копирования данных в TempStream, но в общем случае, когда при перекодировке меняется размер пакета, создание временного потока обязательно. И последняя, казалось бы ненужная команда — обращение к свойству — потоку  Data.Stream, устанавливает текущий указатель потока в начало.

Код в реализации метода DataOut идентичен  — повторная инверсия битов восстанавливает первоначальный результат.

Для тестирования данного приложения прежде всего необходимо зарегистрировать сервер DataIntr командой из меню Delphi Run | Register ActiveX server. Если сервер и клиент находятся на разных компьютерах, то на второй компьютер необходимо скопировать файл DataIntr.dll, например в корневой каталог диска C, и вызвать команду:

C:\WINNT\SYSTEM32\REGSVR32 C:\DataIntr.dll  

Далее нужно скопировать значение константы Class_DataInt, запустить приложение scktsrvr.exe (директория $DELPHI\Bin) на компьютере с COM-сервером, щелкнуть правой кнопкой мыши по пиктограмме этого приложения, вызвать команду Properties и поместить идентификатор фабрики классов в поле Intercept GUID появляющейся диалоговой панели (рис. 3).

Обратите внимание на то, что при реализации этого проекта с самого начала на другом компьютере константа GUID будет другой — GUID отличается тем, что он уникален.

На всех клиентских компьютерах, которые будут обращаться к этому серверу с помощью компонента TSocketConnection, свойство InterceptGUID также должно ссылаться на GUID фабрики классов созданного перекодировщика — иначе клиент получит сообщение о неправильном формате данных (рис. 4).

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

Прежде чем завершить рассмотрение доступа к удаленным COM-серверам с помощью протокола TCP/IP, еще раз напомним об основных достоинствах и недостатках этого метода. К несомненным достоинствам применения этого способа удаленного доступа следует отнести возможность создания COM-клиентов, не требующих никаких клиентских частей и дополнительных настроек на рабочих станциях, кроме поддержки самого протокола TCP/IP, а также отсутствие требований к местоположению клиента и сервера, что делает подобную технологию идеальным решением в случае, если невозможно сделать предположений относительно настроек рабочей станции, на которой будет выполняться подобный клиент, либо если процесс реализации этих настроек является высокозатратным с точки зрения сопровождения приложения (например, в случае территориально распределенного предприятия или при использовании доступа к серверу через Интернет). Из недостатков применения протокола TCP/IP  как технологии удаленного доступа к COM-серверам  следует отметить необходимость  решения проблем, связанных с безопасностью передачи данных, а также проблем, которые могут возникнуть при передаче данных через брандмауэры и proxy-серверы.

КомпьютерПресс 5'2002


Наш канал на Youtube

1999 1 2 3 4 5 6 7 8 9 10 11 12
2000 1 2 3 4 5 6 7 8 9 10 11 12
2001 1 2 3 4 5 6 7 8 9 10 11 12
2002 1 2 3 4 5 6 7 8 9 10 11 12
2003 1 2 3 4 5 6 7 8 9 10 11 12
2004 1 2 3 4 5 6 7 8 9 10 11 12
2005 1 2 3 4 5 6 7 8 9 10 11 12
2006 1 2 3 4 5 6 7 8 9 10 11 12
2007 1 2 3 4 5 6 7 8 9 10 11 12
2008 1 2 3 4 5 6 7 8 9 10 11 12
2009 1 2 3 4 5 6 7 8 9 10 11 12
2010 1 2 3 4 5 6 7 8 9 10 11 12
2011 1 2 3 4 5 6 7 8 9 10 11 12
2012 1 2 3 4 5 6 7 8 9 10 11 12
2013 1 2 3 4 5 6 7 8 9 10 11 12
Популярные статьи
КомпьютерПресс использует