Главная страница Взаимодействие нетривиальных процессов Считывание запроса от клиента 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 (что чаще
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |