Книга: Основы программирования в Linux

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

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

Рассмотрим файл 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);
   }
  } else {
   fprintf(stderr, "Server failed to respondn");
  }
 } else {
  fprintf(stderr, "Server not accepting requestsn");
 }
 return(ret_val);
}

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

static int read_one_response(message_db_t *rec_ptr) {
 int return_code = 0;
 if (!rec_ptr) return(0);
 if (start_resp_from_server()) {
  if (read_resp_from_server(rec_ptr)) {
   return_code = 1;
  }
  end_resp_from_server();
 }
 return(return_code);
}

6. Остальные подпрограммы get_xxx, del_xxx и add_xxx реализованы аналогично функции get_cdc_entry и приводятся здесь для полноты картины. Сначала функция для извлечения дорожек компакт-диска.

cdt_entry get_cdt_entry(const char *cd_catalog_ptr,
 const int track no) {
 cdt_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_cdt_entry;
 strcpy(mess_send.cdt_entry_data.catalog, cd_catalog_ptr);
 mess_send.cdt_entry_data.track_no = track_no;
 if (send_mess_to_server(mess_send)) {
  if (read_one_response(&mess_ret)) {
   if (mess_ret.response == r_success) {
    ret_val = mess_ret.cdt_entry_data;
   } else {
    fprintf(stderr, "%s", mess_ret.error_text);
   }
  } else {
   fprintf(stderr, "Server failed to respondn");
  }
 } else {
  fprintf(stderr, "Server not accepting requestsn");
 }
 return(ret_val);
}

7. Далее две функции для вставки данных, первая для добавления элемента каталога, а вторая — дорожек в базу данных.

int add_cdc_entry(const cdc_entry entry_to_add) {
 message_db_t mess_send;
 message_db_t mess_ret;
 mess_send.client_pid = mypid;
 mess_send.request = s_add_cdc_entry;
 mess_send.cdc_entry_data = entry_to_add;
 if (send_mess_to_server(mess_send)) {
  if (read_one_response(&mess_ret)) {
   if (mess_ret.response == r_success) {
    return(1);
   } else {
    fprintf(stderr, "%s", mess_ret.error_text);
   }
  } else {
   fprintf(stderr, "Server failed to respondn");
  }
 } else {
  fprintf(stderr, "Server not accepting requestsn");
 }
 return(0);
}
int add_cdt_entry(const cdt_entry entry_to_add) {
 message_db_t mess_send;
 message_db_t mess_ret;
 mess_send.client_pid = mypid;
 mess_send.request = s_add_cdt_entry;
 mess send.cdt_entry data = entry_to_add;
 if (send_mess_to_server(mess_send)) {
  if (read_one_response(&mess_ret)) {
   if (mess_ret.response == r_success) {
    return(1);
   } else {
    fprintf(stderr, "%s", mess_ret.error_text);
   }
  } else {
   fprintf(stderr, "Server failed to respondn");
  }
 } else {
  fprintf(stderr, "Server not accepting requestsn");
 }
 return(0);
}

8. В заключение две функции для удаления данных.

int del_cdc_entry(const char *cd_catalog_ptr) {
 message_db_t mess_send;
 message_db_t mess_ret;
 mess_send.client_pid = mypid;
 mess_send.request = s_del_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) {
    return(1);
   } else {
    fprintf(stderr, "%s", mess_ret.error_text);
   }
  } else {
   fprintf(stderr, "Server failed to respondn");
  }
 } else {
  fprintf(stderr, "Server not accepting requestsn");
 }
 return(0);
}
int del_cdt_entry(const char *cd_catalog_ptr, const int track no) {
 message_db_t mess_send;
 message_db_t mess_ret;
 mess_send.client_pid = mypid;
 mess_send.request = s_del_cdt_entry;
 strcpy(mess_send.cdt_entry_data.catalog, cd_catalog_ptr);
 mess_send.cdt_entry_data.track_no = track_no;
 if (send_mess_to_server(mess_send)) {
  if (read_one_response(&mess_ret)) {
   if (mess_ret.response == r_success) {
    return(1);
   } else {
    fprintf(stderr, "%s", mess_ret.error_text);
   }
  } else {
   fprintf(stderr, "Server failed to respondn");
  }
 } else {
  fprintf(stderr, "Server not accepting requestsn");
 }
 return(0);
}

Поиск в базе данных

Функция поиска по ключу компакт-диска сложнее. Пользователь этой функции рассчитывает вызвать ее один раз для начала поиска. Мы удовлетворили его ожидания в главе 7, задавая параметр *first_call_ptr равным true при первом вызове функции, и функция в этом случае возвращает первое найденное совпадение. При последующих вызовах функции поиска указатель *first_call_ptr равен false и возвращаются дальнейшие совпадения, по одному на каждый вызов.

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

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

1. Эта функция не так сложна, как кажется, просто в ней вызываются три функции канала send_mess_to_server, start_resp_from_server и read_resp_fromserver, которые будут рассмотрены в следующем разделе.

cdc_entry search_cdc_entry(const char *cd_catalog_ptr,
 int *first_call_ptr) {
 message_db_t mess_send;
 message_db_t mess_ret;
 static FILE *work_file = (FILE *)0;
 static int entries_matching = 0;
 cdc_entry ret_val;
 ret_val.catalog[0] = '';
 if (!work_file && (*first_call_ptr == 0)) return(ret_val);

2. Далее показан первый вызов для поиска с указателем *first_call_ptr, равным true. Он немедленно приравнивается false, на случай, если вы забыли. Создается временный файл work_file и инициализируется структура сообщения клиенту.

if (*first_call_ptr) {
 *first_call_ptr = 0;
 if (work_file) fclose(work_file);
 work_file = tmpfile();
 if (!work_file) return(ret_val);
 mess_send.client_pid = mypid;
 mess_send.request = s_find_cdc_entry;
 strcpy(mess_send.cdc_entry_data.catalog, cd_catalog_ptr);

3. Теперь приводится проверка условий с тремя уровнями вложенности, заставляющая вызывать функции из файла pipe_imp.c. Если сообщение успешно отправлено на сервер, клиент ждет ответа от сервера. Пока считывания с сервера успешны, совпадения с искомой величиной возвращаются в work_file клиента и наращивается счетчик entries_matching.

 if (send_mess_to_server(mess_send)) {
  if (start_resp_from_server()) {
   while (read_resp_from_server(&mess_ret)) {
    if (mess_ret.response == r_success) {
     fwrite(&mess_ret.cdc_entry_data, sizeof(cdc_entry), 1, work_file);
     entries_matching++;
    } else {
     break;
    }
   } /* while */
  } else {
   fprintf(stderr, "Server not respondingn");
  }
 } else {
  fprintf (stderr, "Server not accepting requestsn");
 }

4. Следующая проверка ищет, есть ли совпадения с заданным значением. Далее вызов fseek переводит указатель в файле work_file на место записи следующей порции данных.

 if (entries_matching == 0) {
  fclose(work_file);
  work_file = (FILE *)0;
  return(ret_val);
 }
 (void)fseek(work_file, 0L, SEEK_SET);

5. Если это не первый вызов функции поиска для данного конкретного элемента, программа проверяет, были ли уже найдены совпадения. В заключение в структуру ret_val читается следующий совпадающий элемент. Предшествующие проверки гарантируют наличие совпадающего элемента.

 } else {
  /* не *first_call_ptr */
  if (entries_matching == 0) {
   fclose(work_file);
   work_file = (FILE *)0;
   return(ret_val);
  }
 }
 fread(&ret_val, sizeof(cdc_entry), 1, work_file);
 entries_matching--;
 return(ret_val);
}

Оглавление книги


Генерация: 1.545. Запросов К БД/Cache: 3 / 0
поделиться
Вверх Вниз