Читать «UNIX: взаимодействие процессов» онлайн - страница 47

Уильям Ричард Стивенс

30   }

31   if ((fd = open(ptr, O_RDONLY)) < 0) {

32    /* ошибка, нужно сообщить клиенту */

33    snprintf(buff + n, sizeof(buff) – n, ": can't open, %s\n",

34     strerror(errno));

35    n = strlen(ptr);

36    Write(writefifo, ptr, n);

37    Close(writefifo);

38

39   } else {

40    /* успешное открытие, копируем файл */

41    while ((n = Read(fd, buff, MAXLINE)) > 0)

42     Write(writefifo, buff, n);

43    Close(fd);

44    Close(writefifo);

45   }

46  }

47 }

Создание канала и открытие его только для записи и только для чтения

10-15 Сервер создает канал FIFO с известным именем, обрабатывая ситуацию, когда такой канал уже существует. Затем этот канал открывается дважды: один раз только для чтения, а второй — только для записи. Дескриптор readfifo используется для приема запросов от клиентов, а дескриптор dummyfd не используется вовсе. Причина, по которой нужно открыть канал для записи, видна из табл. 4.1. Если канал не открыть на запись, то при завершении работы очередного клиента этот канал будет опустошаться и сервер будет считывать 0, означающий конец файла. Пришлось бы каждый раз закрывать канал вызовом close, а затем заново открывать его с флагом O_RDONLY, что приводило бы к блокированию демона до подключения следующего клиента. Мы же всегда будем иметь дескриптор, открытый на запись, поэтому функция read не будет возвращать 0, означающий конец файла, при отсутствии клиентов. Вместо этого сервер просто будет блокироваться при вызове read, ожидая подключения следующего клиента. Этот трюк упрощает код программы-сервера и уменьшает количество вызовов open для канала сервера.

При запуске сервера первый вызов open (с флагом O_RDONLY) приводит к блокированию процесса до появления первого клиента, открывающего канал сервера на запись (см. табл. 4.1). Второй вызов open (с флагом O_WRONLY) не приводит к блокированию, поскольку канал уже открыт на запись. 

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

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

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

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

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

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