KasperskyOS Community Edition 1.0

Пример echo

Пример echo демонстрирует использование IPC-транспорта.

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

В этом разделе

О примере echo

Реализация сущности Client в примере echo

Реализация сущности Server в примере echo

Файлы описаний в примере echo

Сборка и запуск примера echo

В начало

О примере echo

Пример echo описывает простейший случай взаимодействия двух сущностей:

  1. Сущность Client передает сущности Server число (value).
  2. Сущность Server изменяет это число и передает новое число (result) сущности Client.
  3. Сущность Client выводит число result на экран.

Чтобы организовать такое взаимодействие сущностей, потребуется:

  1. Соединить сущности Client и Server, используя init-описание.
  2. Реализовать на сервере интерфейс с единственным методом Ping, который имеет один входной аргумент – исходное число (value) и один выходной аргумент – измененное число (result).

    Описание метода Ping на языке IDL:

    Ping(in UInt32 value, out UInt32 result);

  3. Создать файлы статических описаний на языках EDL, CDL и IDL. С помощью компилятора NK сгенерировать файлы, содержащие транспортные методы и типы (прокси-объект, диспетчеры и т.д.).
  4. В коде сущности Client инициализировать все необходимые объекты (транспорт, прокси-объект, структуру запроса и др.) и вызвать интерфейсный метод.
  5. В коде сущности Server подготовить все необходимые объекты (транспорт, диспетчер компонента и диспетчер сущности и др.), принять запрос от клиента, обработать его и отправить ответ.

Пример echo состоит из следующих исходных файлов:

  • client/src/client.c – реализация сущности Client;
  • server/src/server.c – реализация сущности Server;
  • resources/Server.edl, resources/Client.edl, resources/Ping.cdl, resources/Ping.idl – статические описания;
  • init.yaml – init-описание.
В начало

Реализация сущности Client в примере echo

В коде сущности Client используются транспортные типы и методы, которые будут сгенерированы во время сборки решения компилятором NK на основе IDL-описания интерфейса Ping.

Однако чтобы получить необходимые для реализации сущности описания типов и сигнатуры методов, вы можете воспользоваться компилятором NK непосредственно после создания EDL-описания сущности, CDL-описаний компонентов и IDL-описаний используемых интерфейсов взаимодействия. В результате необходимые типы и сигнатуры методов будут объявлены в сгенерированных файлах *.h.

В реализации сущности Client необходимо:

  1. Получить клиентский IPC-дескриптор соединения (канала), используя функцию локатора сервисов ServiceLocatorConnect().

    На вход нужно передать имя IPC-соединения server_connection, заданное ранее в файле init.yaml.

  2. Инициализировать транспорт NkKosTransport, передав полученный IPC-дескриптор в функцию NkKosTransport_Init().
  3. Получить идентификатор необходимого интерфейса (RIID), используя функцию локатора сервисов ServiceLocatorGetRiid().
  4. На вход нужно передать полное имя реализации интерфейса Ping. Оно состоит из имен экземпляра компонента (Echo.ping) и интерфейса (ping), заданных ранее в Server.edl и Ping.cdl.
  5. Инициализировать прокси-объект, передав транспорт и идентификатор интерфейса в функцию Ping_proxy_init().
  6. Подготовить структуры запроса и ответа.
  7. Вызвать интерфейсный метод Ping_Ping(), передав прокси-объект, а также указатели на запрос и ответ.

client.c

#include <stdio.h>

#include <stdlib.h>

#include <stdint.h>

/* Файлы, необходимые для инициализации транспорта. */

#include <coresrv/nk/transport-kos.h>

#include <coresrv/sl/sl_api.h>

/* Описание интерфейса сервера, по которому обращается клиентская сущность. */

#include <echo/Ping.idl.h>

#include <assert.h>

#define EXAMPLE_VALUE_TO_SEND 777

/* Точка входа в клиентскую сущность. */

int main(int argc, const char *argv[])

{

NkKosTransport transport;

struct echo_Ping_proxy proxy;

int i;

fprintf(stderr, "Hello I'm client\n");

/* Получаем клиентский IPC-дескриптор соединения с именем

* "server_connection". */

Handle handle = ServiceLocatorConnect("server_connection");

assert(handle != INVALID_HANDLE);

/* Инициализируем IPC-транспорт для взаимодействия с серверной сущностью. */

NkKosTransport_Init(&transport, handle, NK_NULL, 0);

/* Получаем Runtime Interface ID (RIID) для интерфейса echo.Ping.ping.

* Здесь ping – имя экземпляра компонента echo.Ping,

* echo.Ping.ping - имя реализации интерфейса Ping. */

nk_iid_t riid = ServiceLocatorGetRiid(handle, "echo.Ping.ping");

assert(riid != INVALID_RIID);

/* Инициализируем прокси-объект, указав транспорт (&transport)

* и идентификатор интерфейса сервера (riid). Каждый метод

* прокси-объекта будет реализован как отправка запроса серверу. */

echo_Ping_proxy_init(&proxy, &transport.base, riid);

/* Структуры запроса и ответа */

echo_Ping_Ping_req req;

echo_Ping_Ping_res res;

/* Тестовый цикл. */

req.value = EXAMPLE_VALUE_TO_SEND;

for (i = 0; i < 10; ++i)

{

/* Вызываем интерфейсный метод Ping.

* Серверу будет отправлен запрос для вызова метода Ping интерфейса

* ping_comp.ping_impl с аргументом value. Вызывающий поток блокируется

* до момента получения ответа от сервера. */

if (echo_Ping_Ping(&proxy.base, &req, NULL, &res, NULL) == rcOk)

{

/* Выводим значение result, содержащееся в ответе

* (result - выходной аргумент метода Ping). */

fprintf(stderr, "result = %d\n", (int) res.result);

/* Помещаем полученное значение result в аргумент value

* для повторной отправки серверу в следующей итерации. */

req.value = res.result;

}

else

fprintf(stderr, "Failed to call echo.Ping.Ping()\n");

}

return EXIT_SUCCESS;

}

В начало

Реализация сущности Server в примере echo

В коде сущности Server используются транспортные типы и методы, которые будут сгенерированы во время сборки решения компилятором NK на основе EDL-описания сущности Server.

Однако чтобы получить необходимые для реализации сущности типы и сигнатуры методов, вы можете воспользоваться компилятором NK непосредственно после создания EDL-описания сущности, CDL-описаний компонентов и IDL-описаний используемых интерфейсов взаимодействия. В результате необходимые типы и сигнатуры методов будут объявлены в сгенерированных файлах *.h.

В реализации сущности server необходимо:

  1. Реализовать метод Ping().

    Сигнатура реализации метода Ping() должна в точности совпадать с сигнатурой интерфейсного метода Ping_Ping(), который объявлен в файле Ping.idl.h.

  2. Получить серверный IPC-дескриптор соединения (канала), используя функцию локатора сервисов ServiceLocatorRegister().

    На вход нужно передать имя IPC-соединения server_connection, заданное ранее в файле init.yaml.

  3. Инициализировать транспорт NkKosTransport, передав полученный IPC-дескриптор в функцию NkKosTransport_Init().
  4. Подготовить структуры запроса и ответа.
  5. Инициализировать dispatch-метод (диспетчер) компонента Ping, используя функцию Ping_component_init().
  6. Инициализировать dispatch-метод (диспетчер) сущности Server, используя функцию Server_entity_init().
  7. Получить запрос, вызвав nk_transport_recv().
  8. Обработать полученный запрос, вызвав диспетчер Server_entity_dispatch().

    Диспетчер вызовет необходимую реализацию метода на основе полученного от клиента идентификатора интерфейса (RIID).

  9. Отправить ответ сущности Client, вызвав nk_transport_reply().

server.c

#include <stdio.h>

#include <stdlib.h>

#include <stdbool.h>

/* Файлы, необходимые для инициализации транспорта. */

#include <coresrv/nk/transport-kos.h>

#include <coresrv/sl/sl_api.h>

/* Описания сущности-сервера на языке EDL. */

#include <echo/Server.edl.h>

#include <assert.h>

/* Тип объекта реализующего интерфейс. */

typedef struct IPingImpl {

struct echo_Ping base; // базовый интерфейс объекта

int step; // дополнительные параметры

} IPingImpl;

/* Реализация метода Ping. */

static nk_err_t Ping_impl(struct echo_Ping *self,

const echo_Ping_req *req,

const struct nk_arena* req_arena,

echo_Ping_res* res,

struct nk_arena* res_arena)

{

IPingImpl *impl = (IPingImpl *)self;

/* Значение value, пришедшее в запросе от клиента, инкрементируем на

* величину шага step и помещаем в аргумент result, который будет

* отправлен клиенту в составе ответа от сервера. */

res->Ping.result = req->Ping.value + impl->step;

return NK_EOK;

}

/* Конструктор объекта IPing.

* step - шаг, то есть число, на которое будет увеличиваться входящее значение. */

static struct echo_Ping *CreateIPingImpl(int step)

{

/* Таблица реализаций методов интерфейса IPing. */

static const struct echo_Ping_ops ops = {

.Ping = Ping_impl

};

/* Объект, реализующий интерфейс. */

static struct IPingImpl impl = {

.base = {&ops}

};

impl.step = step;

return &impl.base;

}

/* Точка входа в сервер. */

int main(void)

{

NkKosTransport transport;

ServiceId iid;

/* Получаем серверный IPC-дескриптор соединения "server_connection". */

Handle handle = ServiceLocatorRegister("server_connection", NULL, 0, &iid);

assert(handle != INVALID_HANDLE);

/* Инициализируем транспорт до клиента. */

NkKosTransport_Init(&transport, handle, NK_NULL, 0);

/* Подготавливаем структуры запроса к сущности server: фиксированную

* часть и арену. Поскольку ни у одного из методов сущности server нет

* аргументов типа sequence, используются только фиксированные части

* запроса и ответа. Арены фактически не используются. Однако в серверные

* методы транспорта (nk_transport_recv, nk_transport_reply) и

* dispatch-метод server_entity_dispatch необходимо передать валидные

* арены запроса и ответа. */

echo_Server_entity_req req;

char req_buffer[echo_Server_entity_req_arena_size];

struct nk_arena req_arena = NK_ARENA_INITIALIZER(req_buffer, req_buffer + sizeof(req_buffer));

/* Подготавливаем структуры ответа: фиксированную часть и арену. */

echo_Server_entity_res res;

char res_buffer[echo_Server_entity_res_arena_size];

struct nk_arena res_arena = NK_ARENA_INITIALIZER(res_buffer, res_buffer + sizeof(res_buffer));

/* Инициализируем диспетчер компонента ping. 3 – величина шага,

* то есть число, на которое будет увеличиваться входящее значение. */

echo_Ping_component component;

echo_Ping_component_init(&component, CreateIPingImpl(3));

/* Инициализируем диспетчер сущности server. */

echo_Server_entity entity;

echo_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, "nk_transport_recv error\n");

} else {

/* Обрабатываем полученный запрос, вызывая реализацию Ping_impl

* запрошенного интерфейсного метода Ping. */

echo_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, "nk_transport_reply error\n");

}

}

while (true);

return EXIT_SUCCESS;

}

В начало

Файлы описаний в примере echo

Описание сущности Client

Сущность Client не реализует ни одного интерфейса, поэтому в ее EDL-описании достаточно объявить имя сущности:

Client.edl

/* Описание сущности Client. */

entity echo.Client

Описание сущности Server

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

Сущность Server содержит экземпляр компонента Ping:

Server.edl

/* Описание сущности Server. */

entity echo.Server

/* Server - именованный экземпляр компонента echo.Ping. */

components {

Server: echo.Ping

}

Компонент Ping содержит реализацию интерфейса Ping:

Ping.cdl

/* Описание компонента Ping. */

component echo.Ping

/* ping - реализация интерфейса Ping. */

interfaces {

ping: echo.Ping

}

Пакет Ping содержит объявление интерфейса Ping:

Ping.idl

/* Описание пакета Ping. */

package echo.Ping

interface {

Ping(in UInt32 value, out UInt32 result);

}

Init-описание

Чтобы сущность Client могла вызвать метод сущности Server, между ними требуется создать соединение (IPC-канал).

Для этого в init-описании укажите, что необходимо запустить сущности Client и Server, а также соединить их:

init.yaml

entities:

- name: echo.Client

connections:

- target: echo.Server

id: server_connection

- name: Server

Поскольку сущность Server указана как - target, она будет выступать в роли серверной сущности, т.е. принимать запросы сущности Client и отвечать на них.

Созданный IPC-канал имеет имя server_connection. С помощью локатора сервисов можно получить дескриптор этого канала.

В начало

Сборка и запуск примера echo

См. "Сборка и запуск примеров".

Схема сборки примера echo выглядит следующим образом:

В начало