Kniga-Online.club

Нейл Мэтью - Основы программирования в Linux

Читать бесплатно Нейл Мэтью - Основы программирования в Linux. Жанр: Программное обеспечение издательство -, год 2004. Так же читаем полные версии (весь текст) онлайн без регистрации и SMS на сайте kniga-online.club или прочесть краткое содержание, предисловие (аннотацию), описание и ознакомиться с отзывами (комментариями) о произведении.
Перейти на страницу:

Важно также не использовать состояние "активного ожидания", чтобы не тратить времени ЦП на ожидание события. Как вы видели, ОС Linux позволяет приостанавливать выполнение в ожидании событий без потребления значительных ресурсов. Следует применять блокирующие свойства каналов для гарантии эффективного использования ЦП. В конце концов, теоретически сервер может ждать в течение многих часов поступления запроса.

Реализация

В предыдущей версии приложения, реализованного в виде единого процесса, с которой вы познакомились в главе 7, для управления данными применялся набор подпрограмм доступа к данным. К ним относились следующие подпрограммы:

int database_initialize(const int new_database);

void database_close(void);

cdc_entry get_cdc_entry(const char *cd_catalog_ptr);

cdt_entry get_cdt_entry(const char *cd_catalog_ptr, const int track_no);

int add_cdc_entry(const cdc_entry entry_to_add);

int add_cdt_entry(const cdt_entry entry_to_add);

int del_cdc_entry(const char *cd_catalog_ptr);

int del_cdt_entry(const char *cd_catalog_ptr, const int track_no);

cdc_entry search_cdc_entry(const char *cd_catalog_ptr,

 int *first_call_ptr);

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

В реализации в виде единого процесса вы можете разделить приложение на две части (рис. 13.6), несмотря на то, что оно компилировалось как единая программа.

Рис. 13.6 

В клиент-серверную версию приложения вы хотите включить несколько именованных каналов и сопроводительный программный код для связи двух основных частей приложения. На рис. 13.7 показана необходимая структура.

Рис. 13.7 

В данной реализации подпрограммы интерфейса и клиента, и сервера помещены в один файл pipe_imp.c. Это сохраняет в едином файле весь программный код, зависящий от применения именованных каналов в клиент-серверной реализации. Форматирование и упаковка передаваемых данных хранятся отдельно от подпрограмм, реализующих именованные каналы. В результате у вас появятся дополнительные файлы исходного текста программы, но с более логичным разделением. Структура вызовов в приложении показана на рис. 13.8.

Рис. 13.8 

Файлы арр_ui.c, client_if.c и pipe_imp.c компилируются и компонуются вместе для получения клиентской программы. Файлы cd_dbm.c, server.c и pipe_imp.c компилируются и компонуются вместе для создания серверной программы. Заголовочный файл cliserv.h действует как заголовочный файл общих определений для связывания обеих программ.

В файлы app_ui.c и cd_dbm.c внесены очень незначительные изменения, в принципе позволяющие разделить приложение на две программы. Поскольку теперь приложение очень большое и существенная часть программного кода не изменилась по сравнению с предыдущей версией, здесь мы покажем только файлы cliserv.h, сlient_if.c и pipe_imp.c.

Заголовочный файл cliserv.h

Сначала рассмотрим cliserv.h. Этот файл определяет клиент-серверный интерфейс. Он необходим и клиентской, и серверной программам.

1. Далее приведены необходимые директивы #include.

#include <unistd.h>

#include <stdlib.h>

#include <stdio.h>

#include <fcntl.h>

#include <limits.h>

#include <sys/types.h>

#include <sys/stat.h>

2. Затем вы определяете именованные каналы. Используйте один канал для сервера и по одному каналу для каждого клиента. Поскольку клиентов может быть несколько, клиентская программа включает идентификатор процесса в имя, таким образом, обеспечивая уникальность канала.

#define SERVER_PIPE "/tmp/server_pipe"

#define CLIENT_PIPE "/tmp/client_%d_pipe"

#define ERR_TEXT_LEN 80

3. Реализуйте команды как перечислимые типы, а не как директивы #define.

Примечание

Это хорошая возможность для компилятора выполнить дополнительную проверку типов и помочь в отладке приложения, т.к. многие отладчики могут показывать имена перечислимых констант, но не имена, определенные директивой #define.

Первый оператор typedef задает тип запроса, отправляемого на сервер; второй описывает тип серверного ответа клиенту.

typedef enum {

 s_create_new_database = 0,

 s_get_cdc_entry,

 s_get_cdt_entry,

 s_add_cdc_entry,

 s_add_cdt_entry,

 s_del_cdc_entry,

 s_del_cdt_entry,

 s_fmd_cdc_entry

} client_request_e;

typedef enum {

 r_success = 0,

 r_failure,

 r_find_no_more

} server_response_e;

4. Далее объявите структуру, которая будет формировать сообщение, передаваемое между двумя процессами в обоих направлениях.

Примечание

Поскольку на самом деле вам не нужно возвращать cdc_entry и cdt_entry в одном ответе, вы могли бы сделать их объединением (union). Но для простоты можно оставить их отдельными элементами, кроме того, в этом случае легче поддерживать программный код.

typedef struct {

 pid_t client_pid;

 client_request_e request;

 server_response_e response;

 cdc_entry cdc_entry_data;

 cdt_entry cdt_entry_data;

 char error_text[ERR_TEXT_LEN + 1];

} message_db_t;

5. В заключение приведены функции интерфейса канала, выполняющие передачу данных и содержащиеся в файле pipe_imp.c. Они делятся на функции серверной и клиентской стороны, в первом и втором блоках соответственно.

int server_starting(void);

void server_ending(void);

int read_request_from_client(message_db_t *rec_ptr);

int start_resp_to_client(const message_db_t mess_to_send);

int send_resp_to_client(const message_db_t mess_to_send);

void end_resp_to_client(void);

int client_starting(void);

void client_ending(void);

int send_mess_to_server(message_db_t mess_to_send);

int start_resp_from_server(void);

int read_resp_from_server(message_db_t *rec_ptr);

void end_resp_from_server(void);

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

Функции интерфейса клиента

Рассмотрим файл clientif.c. Он предоставляет "поддельные" версии подпрограмм доступа к базе данных. Они кодируют запрос в структуре message_db_t и затем применяют подпрограммы из файла pipe_imp.c для передачи запроса серверу. Такой подход позволит вам внести минимальные изменения в первоначальный файл app_ui.c.

Интерпретатор клиента

1. В этом файле реализовано девять функций для работы с базой данных, объявленных в файле cd_data.h. Делает он это передачей запросов серверу и затем возвратом ответа сервера из функции, действуя как посредник. Файл начинается с файлов #include и констант.

#define _POSIX_SOURCE

#include <unistd.h>

#include <stdlib.h>

#include <stdio.h>

#include <fcntl.h>

#include <limits.h>

#include <sys/types.h>

#include <sys/stat.h>

#include "cd_data.h"

#include "cliserv.h"

2. Статическая переменная mypid уменьшает количество вызовов getpid, требуемых в противном случае. Мы применяем локальную функцию read_one_response для устранения дублирующегося программного кода.

static pid_t mypid;

static int read_one_response(message_db_t *rec_ptr);

3. Подпрограммы database_initialize и close все еще вызываются, но теперь используются, соответственно, для инициализации клиентского интерфейса каналов и удаления лишних именованных каналов, когда клиент завершил выполнение.

int database_initialize(const int new_database) {

 if (!client_starting()) return(0);

 mypid = getpid();

 return(1);

}

/* инициализация базы данных */

void database_close(void) {

 client_ending();

}

4. Подпрограмма get_cdc_entry вызывается для получения элемента каталога из базы данных по заданному названию компакт-диска в каталоге. В ней вы кодируете запрос в структуре message_db_t и передаете его на сервер. Далее вы считываете обратно ответ в другую структуру типа message_db_t. Если элемент найден, он включается в структуру message_db_t как структура типа cdc_entry, поэтому вы можете передать соответствующую часть структуры.

cdc_entry get_cdc_entry(const char *cd_catalog_ptr) {

 cdc_entry ret_val;

 message_db_t mess_send;

 message_db_t mess_ret;

 ret_val.catalog[0] = '';

 mess_send.client_pid = mypid;

 mess_send.request = s_get_cdc_entry;

 strcpy(mess_send.cdc_entry_data.catalog, cd_catalog_ptr);

 if (send_mess_to_server(mess_send)) {

  if (read_one_response(&mess_ret)) {

   if (mess_ret.response == r_success) {

    ret_val = mess_ret.cdc_entry_data;

   } else {

    fprintf(stderr, "%s", mess_ret.error_text);

   }

Перейти на страницу:

Нейл Мэтью читать все книги автора по порядку

Нейл Мэтью - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки kniga-online.club.


Основы программирования в Linux отзывы

Отзывы читателей о книге Основы программирования в Linux, автор: Нейл Мэтью. Читайте комментарии и мнения людей о произведении.


Уважаемые читатели и просто посетители нашей библиотеки! Просим Вас придерживаться определенных правил при комментировании литературных произведений.

  • 1. Просьба отказаться от дискриминационных высказываний. Мы защищаем право наших читателей свободно выражать свою точку зрения. Вместе с тем мы не терпим агрессии. На сайте запрещено оставлять комментарий, который содержит унизительные высказывания или призывы к насилию по отношению к отдельным лицам или группам людей на основании их расы, этнического происхождения, вероисповедания, недееспособности, пола, возраста, статуса ветерана, касты или сексуальной ориентации.
  • 2. Просьба отказаться от оскорблений, угроз и запугиваний.
  • 3. Просьба отказаться от нецензурной лексики.
  • 4. Просьба вести себя максимально корректно как по отношению к авторам, так и по отношению к другим читателям и их комментариям.

Надеемся на Ваше понимание и благоразумие. С уважением, администратор kniga-online.


Прокомментировать
Подтвердите что вы не робот:*
Подтвердите что вы не робот:*