Содержание
Пример ping
Пример ping демонстрирует использование политики безопасности решения для управления взаимодействиями между сущностями.
О примере ping
Пример ping включает в себя две сущности: Client
и Server
.
Сущность Server
предоставляет два идентичных метода Ping
и Pong
, которые получают число и возвращают измененное число:
Ping(in UInt32 value, out UInt32 result);
Pong(in UInt32 value, out UInt32 result);
Сущность Client
вызывает оба этих метода в различной последовательности. Если вызов метода запрещен политикой безопасности решения, выводится сообщение Failed to call...
Транспортная часть примера ping практически аналогична таковой для примера echo. Единственное отличие состоит в том, что в примере ping используется два метода (Ping
и Pong
), а не один.
Поскольку использование IPC-транспорта подробно рассмотрено в комментариях к примеру echo, в примере ping оно рассматривается кратко.
В примере ping реализуется политика безопасности решения (security.psl
) на базе модели безопасности модели безопасности Flow.
Состав примера ping
Пример ping состоит из следующих файлов:
client/src/client.c
resources/edl/Client.edl
server/src/server.c
resources/edl/Server.edl
,resources/cdl/Control.cdl
,resources/idl/Connection.idl
init.yaml
security.psl
Реализация сущности Client в примере ping
Сущность Client
для вызывает методы Ping
и Pong
в различной последовательности.
Инициализация транспорта до сервера, использование прокси-объекта и интерфейсных методов, а также назначение файла Connection.idl.h
рассматриваются более подробно в комментариях к файлу client.c в примере echo.
client.c
/* Файлы, необходимые для инициализации транспорта. */
/* Описание интерфейса сервера, по которому обращается клиентская сущность. */
static const char *Tag = "[Client]";
static struct Connection_proxy proxy;
static uint32_t ping(uint32_t value)
{
/* Структуры запроса и ответа */
struct Connection_Ping_req req;
struct Connection_Ping_res res;
req.value = value;
/**
* Вызываем интерфейсный метод Connection_Ping.
* Серверу будет отправлен запрос для вызова метода Ping интерфейса
* control.connectionimpl с аргументом value. Вызывающий поток блокируется
* до момента получения ответа от сервера.
*/
if (Connection_Ping(&proxy.base, &req, NULL, &res, NULL) == rcOk)
{
fprintf(stderr, "%s Ping(%d), result = %d\n", Tag, value, res.result);
value = res.result;
}
else
{
fprintf(stderr, "%s Ping(%d), failed\n", Tag, value);
}
return value;
}
static uint32_t pong(uint32_t value)
{
/* Структуры запроса и ответа */
struct Connection_Pong_req req;
struct Connection_Pong_res res;
req.value = value;
/**
* Вызываем интерфейсный метод Connection_Pong.
* Серверу будет отправлен запрос для вызова метода Pong интерфейса
* controlimpl.connectionimpl с аргументом value. Вызывающий поток блокируется
* до момента получения ответа от сервера.
*/
if (Connection_Pong(&proxy.base, &req, NULL, &res, NULL) == rcOk)
{
fprintf(stderr, "%s Pong(%d), result = %d\n", Tag, value, res.result);
value = res.result;
}
else
{
fprintf(stderr, "%s Pong(%d), failed\n", Tag, value);
}
return value;
}
/* Точка входа в клиентскую сущность. */
int main(int argc, const char *argv[])
{
NkKosTransport transport;
uint32_t value;
int i;
fprintf(stderr, "%s Entity started\n", Tag);
/**
* Получаем клиентский IPC-дескриптор соединения с именем
* "server_connection".
*/
Handle handle = ServiceLocatorConnect("server_connection");
if (INVALID_HANDLE == handle)
{
fprintf(stderr, "%s ServiceLocatorConnect failed\n", Tag);
return EXIT_FAILURE;
}
/* Инициализируем IPC-транспорт для взаимодействия с серверной сущностью. */
NkKosTransport_Init(&transport, handle, NK_NULL, 0);
/* Получаем Runtime Interface ID (RIID) для интерфейса ping.Control.connectionimpl. */
nk_iid_t riid = ServiceLocatorGetRiid(handle, "ping.Control.connectionimpl");
if (INVALID_RIID == riid)
{
fprintf(stderr, "%s ServiceLocatorGetRiid failed\n", Tag);
return EXIT_FAILURE;
}
/**
* Инициализируем прокси-объект, указав транспорт (&transport)
* и идентификатор интерфейса сервера (riid). Каждый метод
* прокси-объекта будет реализован как отправка запроса серверу.
*/
Connection_proxy_init(&proxy, &transport.base, riid);
/* Тестовый цикл. */
value = EXAMPLE_VALUE_TO_SEND;
for (i = 0; i < 5; ++i)
{
value = ping(value);
value = pong(value);
}
value = ping(value);
value = ping(value);
value = pong(value);
value = pong(value);
return EXIT_SUCCESS;
}
Реализация сущности Server в примере ping
Инициализация транспорта до клиента, подготовка структур запроса и ответа, а также назначение файла Server.edl.h
рассматриваются более подробно в комментариях к файлу server.c в примере echo.
server.c
/* Файлы, необходимые для инициализации транспорта. */
/* Описания сущности-сервера на языках EDL, CDL, IDL. */
static const char *Tag = "[Server]";
/* Тип объекта реализующего интерфейс. */
typedef struct ObjectImpl
{
struct Connection base; /* базовый интерфейс объекта */
int step; /* дополнительные параметры */
} ObjectImpl;
/* Реализация метода Ping. */
static nk_err_t Ping_impl(
struct Connection *self,
const struct Connection_Ping_req *req,
const struct nk_arena *req_arena,
struct Connection_Ping_res *res,
struct nk_arena *res_arena)
{
ObjectImpl *impl = (ObjectImpl *)self;
/**
* Значение value, пришедшее в запросе от клиента, инкрементируем на
* величину шага step и помещаем в аргумент result, который будет
* отправлен клиенту в составе ответа от сервера.
*/
res->result = req->value + (unsigned int)impl->step;
return NK_EOK;
}
/* Реализация метода Pong. */
static nk_err_t Pong_impl(
struct Connection *self,
const struct Connection_Pong_req *req,
const struct nk_arena *req_arena,
struct Connection_Pong_res *res,
struct nk_arena *res_arena)
{
ObjectImpl *impl = (ObjectImpl *)self;
/**
* Значение value, пришедшее в запросе от клиента, инкрементируем на
* величину шага step и помещаем в аргумент result, который будет
* отправлен клиенту в составе ответа от сервера.
*/
res->result = req->value + (unsigned int)impl->step;
return NK_EOK;
}
/**
* Конструктор объекта Ping.
* step - шаг, то есть число, на которое будет увеличиваться входящее значение.
*/
static struct Connection *CreateObjectImpl(int step)
{
/* Таблица реализаций методов интерфейса Connection. */
static const struct Connection_ops ops = {.Ping = Ping_impl, .Pong = Pong_impl};
/* Объект, реализующий интерфейс. */
static struct ObjectImpl impl = {.base = {&ops}};
impl.step = step;
return &impl.base;
}
/* Точка входа в сервер. */
int main(void)
{
NkKosTransport transport;
ServiceId iid;
/* Регистрируем соединение и получаем дескриптор для него. */
Handle handle = ServiceLocatorRegister("server_connection", NULL, 0, &iid);
if (INVALID_HANDLE == handle)
{
fprintf(stderr, "%s Failed to register service locator\n", Tag);
return EXIT_FAILURE;
}
/* Инициализируем транспорт до клиента. */
NkKosTransport_Init(&transport, handle, NK_NULL, 0);
/* Подготавливаем структуры запроса: фиксированную часть и арену. */
union Server_entity_req req;
char req_buffer[Server_entity_req_arena_size];
struct nk_arena req_arena = NK_ARENA_INITIALIZER(req_buffer, req_buffer + sizeof(req_buffer));
/* Подготавливаем структуры ответа: фиксированную часть и арену. */
union Server_entity_res res;
char res_buffer[Server_entity_res_arena_size];
struct nk_arena res_arena = NK_ARENA_INITIALIZER(res_buffer, res_buffer + sizeof(res_buffer));
/**
* Инициализируем диспетчер компонента Control. INCREMENT_STEP – значение "шага",
* которое будет прибавляться к входному аргументу value.
**/
struct Control_component component;
Control_component_init(&component, CreateObjectImpl(INCREMENT_STEP));
/* Инициализируем диспетчер сущности Server. */
struct Server_entity entity;
Server_entity_init(&entity, &component);
fprintf(stderr, "Hello I'm server\n");
/* Реализация цикла обработки запросов. */
do
{
/* Сбрасываем буферы с запросом и ответом. */
nk_req_reset(&req);
nk_arena_reset(&req_arena);
nk_arena_reset(&res_arena);
/* Ожидаем запрос от клиента. */
if (nk_transport_recv(&transport.base, &req.base_, &req_arena) != NK_EOK)
{
fprintf(stderr, "%s nk_transport_recv error\n", Tag);
}
else
{
/**
* Обрабатываем полученный запрос, вызывая реализацию connectionimpl
* запрошенного интерфейсного метода Ping.
*/
Server_entity_dispatch(&entity, &req.base_, &req_arena, &res.base_, &res_arena);
}
/* Отправляем ответ. */
if (nk_transport_reply(&transport.base, &res.base_, &res_arena) != NK_EOK)
{
fprintf(stderr, "%s nk_transport_reply error\n", Tag);
}
} while (true);
return EXIT_SUCCESS;
}
Файлы описаний в примере ping
Описание сущности Client
Сущность Client
не реализует ни одного интерфейса, поэтому в EDL-описании достаточно объявить имя сущности.
Client.edl
/* Описание сущности Client. */
entity ping.Client
Описание сущности Server
Сущность Server
реализует интерфейс Connection
, содержащий два метода – Ping
и Pong
. Как и в примере echo, требуется объявить отдельный компонент (например Control
)
, содержащий реализацию интерфейса Connection
.
В EDL-описании сущности Server
необходимо указать, что она содержит экземпляр компонента Control
:
Server.edl
/* Описание сущности Server. */
entity ping.Server
/* controlimpl - именованный экземпляр компонента ping.Control. */
components {
controlimpl : ping.Control
}
В CDL-описании компонента Control
необходимо указать, что он содержит реализацию интерфейса Connection
:
Control.cdl
/* Описание компонента Control. */
component ping.Control
/* connectionimpl - реализация интерфейса ping.Connection. */
interfaces {
connectionimpl : ping.Connection
}
В пакете Connection
необходимо объявить интерфейс Connection
, содержащий два метода – Ping
и Pong
:
Connection.idl
/* Описание пакета Connection. */
package ping.Connection
interface {
Ping(in UInt32 value, out UInt32 result);
Pong(in UInt32 value, out UInt32 result);
}
Init-описание
Чтобы сущность Client
могла вызвать метод сущности Server
, между ними требуется создать IPC-канал.
init.yaml
entities:
- name: ping.Client
connections:
- target: ping.Server
id: server_connection
- name: ping.Server
Канал имеет имя server_connection
. С помощью локатора сервисов можно получить дескриптор этого канала.
Политика безопасности решения в примере ping
Политика безопасности решения в этом примере разрешает запуск всех сущностей и позволяет любой сущности обращаться к сущностям Core
и Server
. При этом обращениями к сущности Server
управляют методы модели безопасности Flow.
Конечный автомат, описанный в конфигурации объекта request_state
модели безопасности Flow, имеет два состояния: ping_next
и pong_next
. Исходное состояние – ping_next
. Разрешены только переходы из ping_next
в pong_next
и обратно.
При вызове методов Ping
и Pong
проверяется текущее состояние объекта request_state
. В состоянии ping_next
разрешен только вызов Ping
, при этом состояние изменится на pong_next
. Аналогично, в состоянии pong_next
разрешен только вызов Pong
, при этом состояние изменится на ping_next
.
Таким образом, методы Ping
и Pong
разрешено вызывать только по очереди.
security.psl
/* Политика безопасности решения для демонстрации использования модели
* безопасности Flow в примере ping */
/* Включение PSL-файлов с формальными представлениями моделей безопасности
* Base и Flow */
use nk.base._
use nk.flow._
/* Создание объекта модели безопасности Flow */
policy object request_state : Flow {
type States = "ping_next" | "pong_next"
config = {
states : ["ping_next" , "pong_next"],
initial : "ping_next",
transitions : {
"ping_next" : ["pong_next"],
"pong_next" : ["ping_next"]
}
}
}
/* Запуск любых сущностей разрешен. */
execute {
grant ()
}
/* Любые запросы разрешены. */
request {
grant ()
}
/* Любые ответы разрешены. */
response {
grant ()
}
/* Включение EDL-файлов */
use EDL kl.core.Core
use EDL ping.Client
use EDL ping.Server
use EDL Einit
/* При запуске сущности Server инициировать эту сущность с конечным автоматом */
execute dst=ping.Server {
request_state.init {sid: dst_sid}
}
/* При вызове метода Ping проверить, что конечный автомат находится в состоянии ping_next.
Если это так, разрешить вызов метода Ping и перевести конечный автомат в состояние pong_next. */
request dst=ping.Server, endpoint=controlimpl.connectionimpl, method=Ping {
request_state.allow {sid: dst_sid, states: ["ping_next"]}
request_state.enter {sid: dst_sid, state: "pong_next"}
}
/* При вызове метода Pong проверить, что конечный автомат находится в состоянии pong_next.
Если это так, разрешить вызов метода Pong и перевести конечный автомат в состояние ping_next. */
request dst=ping.Server, endpoint=controlimpl.connectionimpl, method=Pong {
request_state.allow {sid: dst_sid, states: ["pong_next"]}
request_state.enter {sid: dst_sid, state: "ping_next"}
}