Главная страница  Взаимодействие нетривиальных процессов 

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [ 23 ] 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

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

16 Каждый запрос, принимаемый от клиента, представляет собой одну строку, состоящую из идентификатора процесса, пробела и полного имени требуемого файла. Эта строка считывается функцией геасП i пе (приведенной в [24, с.79]).

Анализ запроса клиента

17-26 Символ перевода строки, возвращаемый функцией геасП ine, удаляется. Этот символ может отсутствовать только в том случае, если буфер был заполнен, прежде чем был обнаружен символ перевода строки, либо если последняя введенная строка не была завершена этим символом. Функция strchr возвращает указатель на первый пробел в этой строке, который затем увеличивается на единицу, чтобы он указывал на первый символ полного имени файла, следующего за пробелом. Полное имя канала клиента формируется из его идентификатора процесса, и этот канал открывается сервером на запись.

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

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

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

fi focli serv/mai ncli ent.с

1 #include fifo.h

2 int

3 mainCint argc. char **argv)

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 - getpidO:

12 snprintf(fifoname. sizeof(fifoname). /tmp/fifo.*ld . (long) pid):

13 if ((mkfifoCfifoname. FILE MODE) < 0) && (errno !- EEXIST))

14 err sys( cant create *s . fifoname):

15 /* инициализация буфера PID и пробелом */

16 snprintf(buff, sizeof(buff). ld . (long) pid);

17 len - strlen(buff);

18 ptr - buff + len:



19 /* считывание полного имени */

20 FgetsCptr. MAXLINE - len. stdin);

21 len = StrlenCbuff); /* fgetsC) гарантирует завершающий О */

22 /* открытие FIFO сервера и запись в него полного имени и PID */

23 writefifo = OpenCSERVJIFO. 0 WRONLY. 0):

24 WriteCwritefifo. buff, len):

25 /* открытие созданного FIFO: блокирование до открытия его сервером */

26 readfifo - OpenCfifoname. 0 RDONLY; 0):

27 /* считывание из канала IPC. запись в stdout */

28 while С Cn - ReadСreadfifo. buff. MAXLINE)) > 0)

29 WriteCSTDOUTJILENO. buff, n):

30 CloseCreadfifo):

31 UnlinkCfifoname):

32 exitCO):

33 }

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

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

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

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

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

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

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

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

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

/etc/shadow файл, который нам нельзя читать

/etc/shadow: cant 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.SPid

Solaris % echo $Pid /etc/inet/ntp.conf > /tmp/fifo.serv Solaris % cat < /tmp/fifo.SPid

multicastclient 224.0.1.1 driftfile /etc/inet/ntp.drift Solaris % rm /tmp/fifo.SPid

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

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

Solaris % cat > /tmp/fifo.serv /no/process/id

999999 /invalid/process/id

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

Solaris % server

bogus request: /no/process/id

cannot open: /tmp/fifo.999999

Атомарность записи в FIFO

Наша простейшая пара клиент-сервер позволяет наглядно показать важность наличия свойства атомарности записи в программные каналы и FIFO. Предположим, что два клиента посылают серверу запрос приблизительно в один и тот же момент. Первый клиент отправляет следующую строку:

1234 /etc/inet/ntp.conf второй:

9876 /etc/passwd

Предполагая, что каждый клиент помещает данные в FIFO за один вызов wri te и каждая строка имеет размер, не превышающий величины PIPE BUF (что чаще



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 [ 23 ] 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186

© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования.