- •7. Управление внешними коммуникациями в ос
- •7.1. Протоколы тср/ip
- •7.1.1. Определение и достоинства протокола tcp/ip
- •7.1.2. Архитектура tcp/ip
- •7.1.3. Соответствие между моделями tcp/ip и iso osi
- •7.2. Протокол ip
- •7.2.1. Структура заголовка ip-пакета
- •7.3. Протокол udp
- •7.4. Протокол tcp
- •7.4.1. Структура tcp-сегмента
- •7.4.2. Этапы tcp-сеанса
- •7.5. Программные средства поддержки сетей
- •7.5.1. Состав программных средств поддержки сетей
- •1. Почтовые ящики Windows
- •2. Именованные каналы Windows
- •3. Удаленные вызовы процедур - rpc
- •7.5.2. Программные средства работы с сокетами
- •7.6. Сетевое программное обеспечение уровня приложений
- •7.6.1. Протокол ftp
- •7.6.2. Протокол smtp
- •Раздел 7
2. Именованные каналы Windows
Именованные каналы предоставляют приложениям надежную двухстороннюю связь.
Именованный канал создается вызовом:
NP = CreateNamedPipe("\\\\.\\pipe\\mypipe",
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT,
1,
0,
0,
INFINITE,
NULL);
Приложение, создавшее такой канал, становится сервером.
Сервер переходит в режим ожидания запросов клиента с помощью вызова:
ConnectNamedPipe(NP, NULL);
Поскольку вызов функции ConnectNamedPipe() приводит к ожиданию процесса, то лучше ее вызывать в отдельном потоке.
Клиент подключается к каналу с помощью вызова:
NP = CreateFile("\\\\pc_name\\pipe\\mypipe",
GENERIC_READ|GENERIC_WRITE,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
После того, как соединение между сервером и клиентом будет установлено, они могут обмениваться данными с помощью вызовов ReadFile() и WriteFile(), аналогично предыдущему случаю.
Аналогичным же образом (CloseHandle()) канал закрывается.
3. Удаленные вызовы процедур - rpc
Вспомним опять традиционную модель процедурного программирования. Прикладная программа выглядит как последовательность вызовов процедур, следующим образом:
{
...
Call1();
Call2();
...
}
Удаленные вызовы процедур – это средство расширения традиционной модели программирования на распределенные системы.
Хотя и можно использовать RPC сами по себе, чаще они все-таки являются средством реализации COM и DCOM.
Понятия интерфейса и глобально уникальных идентификаторов являются фундаментальными для RPC, COM и DCOM.
В технологии RPC также есть клиент и сервер. При этом запрос от клиента выглядит по форме как вызов процедуры, но эта процедура включает в себя всю последовательность действий по передаче данных на сервер, обработке запроса и посылке ответа клиенту.
Процедурный интерфейс скрывает все эти действия.
Базовым недостатком RPC является синхронность. Т. е. когда клиент обращается с RPC-запросами к процедуре сервера, то он блокируется до момента, пока сервер не выполнит функцию и не возвратит результаты.
Существуют усовершенствованные RPC, обеспечивающие асинхронные вызовы. В них для оповещения клиента о выполнении RPC используются обратные вызовы. Хотя структура синхронной программы существенно проще асинхронной.
Программа, реализующая технологию клиент/сервер с помощью RPC, будет выглядеть следующим образом:
{
...
CSInterface(Call1);
CSInterface(Call2);
...
}
Процедура CSInterface() - интерфейсная процедура, первый аргумент которой - это, обычно, имя серверной процедуры, которую необходимо выполнить. Могут передаваться и дополнительные параметры в процедуру CSInterface().
Структура удаленного вызова процедуры может выглядеть следующим образом:
Обобщенная структура интерфейсной процедуры, написанной на псевдокоде, может выглядеть следующим образом:
void CSInterface(Procedure P)
{
switch P {
case Call1: {
Создание структур данных для передачи по сети;
Посылка запроса на сервер;
Выполнение процедуры Call1 на сервере;
Подготовка ответа;
Посылка ответа клиенту;
Прием ответа;
}
case Call2: {
...
}
}
}
Синхронный характер процедурного интерфейса иллюстрируется следующим рисунком:
Клиент Сервер
------------------- -------------------
| | | |
|1. ServerRequest | -----> 2. ------> |-----> 3. |
| (приостанов | | . |
| выполнения) | | . |
| 6.<-----| <----- 5. <------ |4. RespondClient;|
| . | | |
| . | | |
------------------- -------------------
Цифры показывают порядок происходящих событий:
Запрос;
Передача запроса;
Начало обработки запроса;
Ответ;
Посылка ответа;
Получение результата клиентом.
Реализация без ожидания иллюстрируется следующим образом:
Клиент Сервер
------------------- -------------------
| | | |
|1. ServerRequest | -----> 2. ------> |-----> 3. |
| . | | . |
| . | | . |
| 6.<-----| <----- 5. <------ |4. RespondClient;|
| . | | |
| . | | |
------------------- -------------------
Клиент дает запрос серверу и асинхронно продолжает работать одновременно с серверным приложением. Позднее клиент либо запрашивает сервер, либо получает уведомление через специальное средство процедуру обратного вызова.
Структура программы клиента с процедурой обратного вызова включает в себя следующие элементы:
1) Описание процедуры обратного вызова. Содержание процедуры обратного вызова полностью определяется характером задач уровнем протокола. На сетевом уровне (3) - это может быть простая установка флага, извещающего клиента о завершении операции. На уровне презентации (6), например, в протоколе DDE, - это достаточно сложные действия.
void CallbackProc()
{
...
}
2) Предусматривается передача адреса процедуры CallbackProc обратного вызова интерфейсной процедуре CSInterface. Если в качестве фактического параметра передается NULL, то выполнение процедуры CSInterface происходит синхронно, а если передается не NULL, а адрес CallbackProc, то выполнение происходит асинхронно.
Т.е. по выполнению запроса коммуникационная среда сама вызывает процедуру CallbackProc, и тем самым оповещает клиента о выполнении запроса.
Приведенная технология асинхронного взаимодействия клиента и сервера на основе процедуры обратного вызова широко используется на всех уровнях коммуникаций.
Создание RPC начинается с описания интерфейса, экспортируемого сервером.
Интерфейс создается на языке описания интерфейсов IDL (Interface Definition Language).
Язык описания интерфейса имеет строго определенные правила. Например:
Все функции имеют тип void;
Параметры сопровождаются модификаторами [in], [out] (входной, выходной);
Типы данных стандартные (int, long). ANSI
Интерфейс содержит глобальный уникальный идентификатор (GUID). С его использованием RPC регистрируется в системе. На основе регистрации идет поиск машины, на которой RPC выполняется, при вызове RPC процессом-клиентом.
GUID создается специальной программой, например, uuidgen.
Пример результата работы этой программы
96fa7b8c-92f7-430b-b552-bdb73188c853
Пример интерфейса:
[ uuid (96fa7b8c-92f7-430b-b552-bdb73188c853),
version (1.0),
pointer_default (unique) ]
interface AboutRemoteSystem
{
void get_disk_free_space([in, string, size_is (256)] char* RootPathName,
[out] long* SecPerClus,
[out] long* BytesPerSec,
[out] long* NumFreeClus);
}
Текст интерфейса обрабатывается специальным компилятором, в результате чего формируются так называемые суррогаты (stubs) клиента и сервера.
Если, например, файл интерфейса имеет имя syscommands.idl, то компилятор сформирует файлы:
syscommands.h;
syscommands_s.c – суррогат сервера;
syscommands_c.c – суррогат клиента.
Файлы syscommands.h, syscommands_s.c необходимо включить в проект сервера, а файлы syscommands.h, syscommands_c.c необходимо включить в проект клиента.
Суррогат клиента выполняет следующие действия:
Преобразует параметры в вид, пригодный для передачи по каналу;
Устанавливает соединение с сервером;
Передает серверу данные;
Ждет ответа от сервера;
Получает данные;
Преобразует данные в вид, пригодный клиенту;
Передает данные клиенту.
Суррогат сервера выполняет следующие действия:
Ждет запросы от клиентов;
Получив запрос, преобразует принятые данные в вид, пригодный для выполнения процедуры;
Выполняет процедуру;
Готовит данные-результаты к передаче по каналу;
Передает данные по каналу.
Внешне программа клиента, вызывающего удаленную процедуру, очень похожа на программу, вызывающую локальную процедуру.
Фрагмент клиента представлен ниже:
char Drive[] = "C:\\";
int SecPerClus, BytesPerSec, NumFreeClus;
get_disk_free_space (Drive, &SecPerClus, &BytesPerSec, &NumFreeClus);
Программа сервер существенно сложнее реализуется, чем программа-клиент.
Этапы инициализации сервера следующие:
Регистрирует в библиотеке RPC коммуникационный протокол, который будет поддерживать сервер (TCP/IP или именованные каналы):
RpcServerUseProtseqEp();
Регистрирует в библиотеке RPC интерфейс, который поддерживает сервер:
RpcServerRegisterIf();
Получает набор ссылок, через которые сервер будет взаимодействовать с библиотекой RPC:
RpcServerInqBindings(&pBindVector);
Через полученные ссылки публикует свои возможности в специальной базе данных “RPC name service database”. Клиент запрашивает специальный сервис “RPC name service” при поиске сервера, способного выполнить запрос.
RpcNsBindingExport();
Переходит в состояние ожидания запросов:
RpcServerListen();
Непосредственное соединение обеспечивают специальные библиотеки. При этом возможна передача как по протоколам TCP/IP, так и через именованные каналы (Windows).