ПРИМЕЧАНИЕ
При такой схеме может возникнуть проблема в случае «гибели» клиента, потому что тогда сообщения останутся в его очереди навсегда (по крайней мере до перезагрузки ядра или явного удаления очереди другим процессом).
Нижеследующие заголовочные файлы и функции не претерпевают изменений по сравнению с предыдущими версиями:
■ mesg.h (листинг 4.12);
■ svmsg.h (листинг 6.7);
■ функция main сервера (листинг 6.12);
■ функция mesg_send (листинг 4.13).
Функция main клиента приведена в листинге 6.16; она слегка изменилась по сравнению с листингом 6.14. Мы открываем очередь сервера с известным ключом (MQ_KEY1) и создаем нашу собственную очередь с ключом IPC_PRIVATE. Два идентификатора этих очередей становятся аргументами функции client (листинг 6.17). После завершения работы клиента его персональная очередь удаляется.
//svmsgmpxnq/client_main.с
1 #include "svmsg.h"
2 void client(int, int);
3 int
4 main(int argc, char **argv)
5 {
6 int readid, writeid;
7 /* сервер должен создать свою очередь */
8 writeid = Msgget(MQ_KEY1, 0);
9 /* мы создаем свою собственную очередь */
10 readid = Msgget(IPC_PRIVATE, SVMSG_MODE | IPC_CREAT);
11 client(readid, writeid);
12 /* и удаляем нашу собственную очередь */
13 Msgctl(readid, IPC_RMID, NULL);
14 exit(0);
15 }
//svmsgmpxnq/client.с
1 #include "mesg.h"
2 void
3 client(int readid, int writeid)
4 {
5 size_t len;
6 ssize_t n;
7 char *ptr;
8 struct mymesg mesg;
9 /* инициализируем буфер идентификатором очереди и пробелом */
10 snprintf(mesg.mesg_data, MAXMESGDATA, "%d ", readid);
11 len = strlen(mesg.mesg_data);
12 ptr = mesg.mesg_data + len;
13 /* считываем имя файла */
14 Fgets(ptr, MAXMESGDATA – len, stdin);
15 len = strlen(mesg.mesg_data);
16 if (mesg.mesg_data[len-1] == '\n')
17 len--; /* удаляем перевод строки fgets */
18 mesg.mesg_len = len;
19 mesg.mesg_type = 1;
20 /* отправляем идентификатор очереди и имя файла серверу */
21 Mesg_send(writeid, mesg);
22 /* считываем ответ из нашей очереди и записываем его в stdout */
23 while ((n = Mesg_recv(readid, mesg)) 0)
24 Write(STDOUT_FILENO, mesg.mesg_data, n);
25 }
В листинге 6.17 приведен текст функции client. Эта функция практически идентична функции из листинга 6.15, но вместо передачи идентификатора процесса клиента на сервер направляется идентификатор очереди клиента. Тип сообщения в структуре mesg остается равным 1, поскольку это значение устанавливается для сообщений, передаваемых в обе стороны.
В листинге 6.19 приведена функция server. Главное отличие от листинга 6.13 в том, что эта функция представляет собой бесконечный цикл, в котором для каждого нового клиента вызывается fork.
10 Поскольку для каждого клиента порождается отдельный процесс, нужно позаботиться о процессах-зомби. В разделах 5.9 и 5.10 [24] об этом говорится подробно. Здесь мы просто установим обработчик для сигнала SIGCHLD, и наша функция sig_chld (листинг 6.18) будет вызываться при завершении работы дочернего процесса.
12-18 Породивший процесс сервера блокируется в вызове mesg_recv, ожидая появления сообщения от очередного клиента.