Книга: UNIX: взаимодействие процессов

Открытие файла и отправка его в FIFO клиента

Открытие файла и отправка его в FIFO клиента

27-44 Оставшаяся часть кода пpoгрaммы-cepвepa аналогична функции server из листинга 4.3. Программа открывает файл; если при этом возникает ошибка — клиенту отсылается сообщение о ней. Если открытие файла завершается успешно, его содержимое копируется в канал клиента. После завершения копирования открытый сервером «конец» (дескриптор) канала клиента должен быть закрыт с помощью функции close, чтобы функция read вернула пpoгрaммe-клиeнтy значение 0 (конец файла). Сервер не удаляет канал клиента; клиент должен самостоятельно позаботиться об этом после приема от сервера символа конца файла. Текст пpoгрaммы-клиeнтa приведен в листинге 4.11.

Листинг 4.11. Клиент, связывающийся с сервером (листинг 4.10) с помощью канала FIFO

//fifocliserv/mainclient.с
1  #include "fifo.h"
2  int
3  main(int argc, char **argv)
4  {
5   int readfifo, writefifo;
6   size_t len;
7   ssize_t n;
8   char *ptr, fifoname[MAXLINE], buff[MAXLINE];
9   pid_t pid;
10  /* создание FIFO с включением в его имя PID */
11  pid = getpid();
12  snprintf(fifoname, sizeof(fifoname), "/tmp/fifo,%ld", (long) pid):
13  if ((mkfifo(fifoname, FILE_MODE) < 0) && (errno != EEXIST))
14   err_sys("can't create %s", fifoname);
15  /* инициализация буфера PID и пробелом */
16  snprintf(buff, sizeof(buff), "%ld ", (long) pid);
17  len = strlen(buff);
18  ptr = buff + len;
19  /* считывание полного имени */
20  Fgets(ptr, MAXLINE – len, stdin);
21  len = strlen(buff); /* fgets() гарантирует завершающий 0 */
22  /* открытие FIFO сервера и запись в него полного имени и PID */
23  writefifo = Open(SERV_FIFO, O_WRONLY, 0);
24  Write(writefifo, buff, len);
25  /* открытие созданного FIFO; блокирование до открытия его сервером */
26  readfifo = Open(fifoname, O_RDONLY; 0);
27  /* считывание из канала IPC, запись в stdout */
28  while ((n = Read(readfifo, buff, MAXLINE)) > 0)
29   Write(STDOUT_FILENO, buff, n);
30  Close(readfifo);
31  Unlink(fifoname);
32  exit(0);
33 }

Создание канала

10-14 Идентификатор процесса клиента содержится в имени создаваемого им канала.

Формирование строки запроса

15-21 Запрос клиента состоит из его идентификатора процесса, одного пробела, полного имени запрашиваемого им файла и символа перевода строки. Строка запроса формируется в массиве buff, причем имя файла считывается из стандартного потока ввода.

Открытие канала сервера и отправка запроса

22-24 Клиент открывает канал сервера и записывает в него строку запроса. Если клиент окажется первым с момента запуска сервера, вызов open разблокирует сервер, заблокированный после сделанного им вызова open (с флагом O_RDONLY).

Прием содержимого файла или сообщения об ошибке от сервера

25-31 Ответ сервера считывается из канала и записывается в стандартный поток вывода, после чего канал клиента закрывается и* удаляется.

Сервер может быть запущен в одном из окон, а клиент — в другом, и программа будет работать так, как мы и рассчитывали. Ниже мы приводим только текст, выводимый клиентом:

solaris % mainclient /etc/shadow          файл, который нам нельзя читать
/etc/shadow: can't open. Permission denied
solaris % mainclient /etc/inet/ntp.conf  файл из двух строк
multicastclient 224.0.1.1
driftfile /etc/inet/ntp.drift
 

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

solaris % Pid=$$
solaris % mkfifo /tmp/fifo.$Pid
solaris % echo "$Pid /etc/inet/ntp.conf" > /tmp/fifo.serv
solaris % cat < /tmp/fifo.$Pid
multicastclient 224.0.1.1
driftfile /etc/inet/ntp.drift
solaris % rm /tmp/fifo.$Pid

Мы отсылаем серверу идентификатор процесса текущей копии интерпретатора и полное имя файла одной командой интерпретатора (echo) и считываем из канала сервера результат с помощью другой команды (cat). Между выполнением этих двух команд может пройти произвольный промежуток времени. Таким образом, сервер помещает содержимое файла в канал, а клиент затем запускает команду cat, чтобы считать оттуда данные. Может показаться, что данные каким-то образом хранятся в канале, хотя он не открыт ни одним процессом. На самом деле все не так. После закрытия пpoгрaммнoгo канала или FIFO последним процессом с помощью команды close все данные, в нем находящиеся, теряются. В нашем примере сервер, считав строку запроса от клиента, блокируется при попытке открыть канал клиента, потому что клиент (наша копия интерпретатора) еще не открыл его на чтение (вспомним табл. 4.1). Только после вызова cat некоторое время спустя канал будет открыт на чтение, и тогда сервер разблокируется. Кстати, таким образом осуществляется атака типа «отказ в обслуживании» (denial-of-service attack), которую мы обсудим в следующем разделе.

Использование интерпретатора позволяет провести простейшую проверку способности сервера обрабатывать ошибки. Мы можем отправить серверу строку без идeнтификaтopa процесса или отослать ему такой идентификатор, которому не соответствует никакой канал FIFO в каталоге /tmp. Например, если мы запустим сервер и введем нижеследующие строки:

solaris % cat > /tmp/fifo.serv /no/process/id
999999 /invalid/process/id

то сервер выдаст текст:

solaris % server
bogus request: /no/process/id
cannot open: /tmp/fifo.999999

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


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