Книга: UNIX: взаимодействие процессов
Пример
Разделы на этой странице:
- Листинг 15.15. Клиент для сервера, передающего дескриптор
- Открываем дверь, считываем полное имя файла
- Подготовка аргументов и указателя на буфер возврата
- Вызов процедуры сервера и проверка результатов
- Считывание дескриптора и копирование файла
- Листинг 15.16. Процедура сервера, открывающая файл и возвращающая клиенту дескриптор
- Открытие файла для клиента
- Успешное открытие файла
Пример
Изменим наш пример с сервером файлов таким образом, чтобы сервер открывал файл, передавал дескриптор клиенту, а клиент копировал содержимое файла в стандартный поток вывода. На рис. 15.4 приведена схема приложения. В листинге 15.15 приведен текст программы клиента.
Листинг 15.15. Клиент для сервера, передающего дескриптор
//doors/clientfd1.c
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 int door, fd;
6 char argbuf[BUFFSIZE], resbuf[BUFFSIZE], buff[BUFFSIZE];
7 size_t len, n;
8 door_arg_t arg;
9 if (argc != 2)
10 err_quit("usage: clientfd1 <server-pathname>");
11 door = Open(argv[1], O_RDWR); /* открываем дверь */
12 Fgets(argbuf, BUFFSIZE, stdin); /* считываем полное имя открываемого файла */
13 len = strlen(argbuf);
14 if (argbuf[len-1] == 'n')
15 len--;
16 /* подготавливаем аргумент и указатель на результат */
17 arg.data_ptr = argbuf; /* аргумент-данные */
18 arg.data_size = len + 1; /* размер данных */
19 arg.desc_ptr = NULL;
20 arg.desc_num = 0;
21 arg.rbuf = resbuf; /* результаты-данные */
22 arg.rsize = BUFFSIZE; /* размер возвращаемых данных */
23 Door_call(door, &arg); /* вызов процедуры сервера */
24 if (arg.data_size != 0)
25 err_quit("%.*s", arg.data_size, arg.data_ptr);
26 else if (arg.desc_ptr == NULL)
27 err_quit("desc_ptr is NULL");
28 else if (arg.desc_num != 1)
29 err_quit("desc_num = %d", arg.desc_num);
30 else if (arg.desc_ptr->d_attributes != DOOR_DESCRIPTOR)
31 err_quit("d_attributes = %d", arg.desc_ptr->d_attributes);
32 fd = arg.desc_ptr->d_data.d_desc.d_descriptor;
33 while((n = Read(fd, buff, BUFFSIZE)) > 0)
34 Write(STDOUT_FILENO, buff, n);
35 exit(0);
36 }
Открываем дверь, считываем полное имя файла
9-15 Имя файла, связанного с дверью, принимается в качестве аргумента командной строки. Имя файла, который должен быть открыт и выведен, считывается из стандартного потока ввода, а завершающий символ перевода строки удаляется.
Подготовка аргументов и указателя на буфер возврата
16-22 Подготавливается структура door_arg_t. К размеру имени файла мы добавляем единицу, чтобы сервер мог дополнить его завершающим нулем.
Вызов процедуры сервера и проверка результатов
23-31 Мы вызываем процедуру сервера и проверяем результат. Должен возвращаться только один дескриптор и никаких данных. Вскоре мы увидим, что сервер возвращает данные (сообщение об ошибке) только в том случае, если он не может открыть файл. В этом случае функция err_quit выводит сообщение об ошибке.
Считывание дескриптора и копирование файла
32-34 Дескриптор извлекается из структуры door_desc_t, и файл копируется в стандартный поток вывода.
В листинге 15.16 приведен текст процедуры сервера. Функция main по сравнению с листингом 15.3 не изменилась.
Листинг 15.16. Процедура сервера, открывающая файл и возвращающая клиенту дескриптор
//doors/serverfd1.c
1 #include "unpipc.h"
2 void
3 servproc(void *cookie, char *dataptr, size_t datasize,
4 door_desc_t *descptr, size_t ndesc)
5 {
6 int fd;
7 char resbuf[BUFFSIZE];
8 door_desc_t desc;
9 dataptr[datasize-1] = 0; /* завершающий О */
10 if ((fd = open(dataptr, O_RDONLY)) == –1) {
11 /* ошибка, нужно сообщить клиенту */
12 snprintf(resbuf, BUFFSIZE, "%s: can't open, %s",
13 dataptr, strerror(errno));
14 Door_return(resbuf, strlen(resbuf), NULL, 0);
15 } else {
16 /* ОК, возвращаем дескриптор */
17 desc.d_data.d_desc.d_descriptor = fd;
18 desc.d_attributes = DOOR_DESCRIPTOR;
19 Door_return(NULL, 0, &desc, 1);
20 }
21 }
Открытие файла для клиента
9-14 Мы завершаем полное имя файла клиента нулем и делаем попытку открыть этот файл вызовом open. Если возникает ошибка, сообщение о ней возвращается клиенту.
Успешное открытие файла
15-20 Если файл был успешно открыт, клиенту возвращается только его дескриптор.
Запустим сервер и укажем ему имя двери /tmp/fd1, а затем запустим клиент:
solaris % clientfd1 /tmp/fd1
/etc/shadow
/etc/shadow: can't open. Permission denied
solaris % clientfd1 /tmp/fd1
/no/such/file
/no/such/file: can't open. No such file or directory
solaris % clientfd1 /tmp/fd1
/etc/ntp.conf файл из двух строк
multicastclient 224.0.1.1
driftfile /etc/ntp.drift
В первых двух случаях мы указываем имя файла, приводящее к возврату сообщения об ошибке. В третий раз сервер передает клиенту дескриптор файла из двух строк, который благополучно выводится.
ПРИМЕЧАНИЕ
Существует проблема, связанная с передачей дескриптора через дверь. Чтобы она проявилась в нашем примере, достаточно добавить вызов printf к процедуре сервера сразу после успешного вызова open. Вы увидите, что значение дескриптора каждый раз увеличивается на единицу. Проблема в том, что сервер не закрывает дескрипторы после передачи их клиенту. Сделать это, вообще говоря, нелегко. Логично было бы выполнять закрытие дескриптора после возврата из door_return, после успешной отправки дескриптора клиенту, но возврата из door_return не происходит! Если бы мы использовали sendmsg для передачи дескриптора через доменный сокет Unix или ioctl для передачи дескриптора через канал в SVR4, мы могли бы закрыть его после возврата из sendmsg или ioctl. Однако с дверьми все по-другому, поскольку возврата из функции door_return не происходит. Единственный способ обойти проблему заключается в том, что процедура сервера должна запоминать все открытые дескрипторы и закрывать их некоторое время спустя, что несколько запутывает код.
Эта проблема должна быть исправлена в Solaris 2.7 добавлением атрибута DOOR RELEASE. Отправитель устанавливает поле d_attributes равным DOOR DESCRIPTOR | DOOR_RELEASE, что говорит системе о необходимости закрывать дескриптор после передачи его клиенту.
- 15.1. Введение
- 15.2. Функция door_call
- 15.3. Функция door_create
- 15.4. Функция door_return
- 15.5. Функция door_cred
- 15.6. Функция door_info
- 15.7. Примеры
- 15.8. Передача дескрипторов
- Пример
- 15.9. Функция door server_create
- 15.10. Функции door_bind, door unbind и door_revoke
- 15.11. Досрочное завершение клиента или сервера
- 15.12. Резюме
- Упражнения
- Пример установочного скрипта
- Пример из практики
- ПРИМЕР ПРОСТОЙ ПРОГРАММЫ НА ЯЗЫКЕ СИ
- Примеры получения статистики
- Пример применения метода «пять почему»
- Пример 12-8. Частота встречаемости отдельных слов
- 1.2.5. Пример программы
- Пример 17-10. Блочный комментарий
- Примеры
- 2. Пример создания базового отношения в записи на псевдокоде
- Пример 9-8. Содержимое $* и $@, когда переменная $IFS -- пуста
- Часть I На примере денег