Каждое сообщение содержит в себе информацию о своем типе (mesg_type), причем значение этой переменной должно быть больше нуля. Пока мы будем игнорировать это поле в записи, но вернемся к нему в главе 6, где описываются очереди сообщений System V. Каждое сообщение также обладает длиной, кoтopая может быть и нулевой. Структура mymesg позволяет предварить каждое сообщение информацией о его типе и длине вместо использования символа перевода
строки для сигнализации конца сообщения. Ранее мы отметили два преимущества этого подхода: получатель не должен сканировать все принятые байты в поисках конца сообщения и отсутствует необходимость исключать появление разделителя в самих данных.
На рис. 4.13 изображен вид структуры mymesg и ее использование с каналами, FIFO и очередями сообщений System V.
Рис. 4.13. Структура mymesg
Мы определяем две функции для отправки и приема сообщений. В листинге 4.13 приведен текст функции mesg_send, а в листинге 4.14 — функции mesg_recv.
Теперь для каждого сообщения функция read вызывается дважды: один раз для считывания длины, а другой — для считывания самого сообщения (если его длина больше 0).
ПРИМЕЧАНИЕ
Внимательные читатели могли заметить, что функция mesg_recv проверяет наличие всех возможных ошибок и прекращает работу при их обнаружении. Однако мы все же определили функцию-обертку Mesg_recv и вызываем из наших программ именно ее — для единообразия.
Изменим теперь функции client и server, чтобы воспользоваться новыми функциями mesg_send и mesg_recv. В листинге 4.15 приведен текст функции-клиента.
Листинг 4.15. Функция client с использованием сообщений
//pipemesg/client.c
1 #include "mesg.h"
2 void
3 client(int readfd, int writefd)
4 {
5 size_t len;
6 ssize_t n;
7 struct mymesg mesg;
8 /*
считывание полного имени */
9 Fgets(mesg.mesg_data, MAXMESGDATA, stdin);
10 len = strlen(mesg.mesg_data);
11 if (mesg.mesg_data[len-1] == '\n')
12 len--; /* удаление перевода строки из fgets */
13 mesg.mesg_len = len;
14 mesg.mesg_type = 1;
15 /* запись полного имени в канал IPC */
16 Mesg_send(writefd, &mesg);
17 /* считывание из канала IPC. запись в stdout */
18 while ( (n = Mesg_recv(readfd, &mesg)) > 0)
19 Write(STDOUT_FILENO, mesg.mesg_data, n);
20 }
Считывание имени файла и отправка его серверу
8-16 Полное имя считывается из стандартного потока ввода и затем отправляется на сервер с помощью функции mesg_send.
Считывание содержимого файла или сообщения об ошибке от сервера
17-19 Клиент вызывает функцию mesg_recv в цикле, считывая все приходящие от сервера сообщения. По соглашению, когда mesg_recv возвращает нулевую длину сообщения, это означает конец передаваемых сервером данных. Мы увидим, что сервер добавляет символ перевода строки к каждому сообщению, отправляемому клиенту, поэтому пустая строка будет иметь длину сообщения 1. В листинге 4.16 приведен текст функции-сервера.
Листинг 4.16. Функция server, использующая сообщения
//pipemesg/server.c
1 #include "mesg.h"
2 void
3 server(int readfd, int writefd)
4 {
5 FILE *fp;
6 ssize_t n;
7 struct mymesg mesg;
8 /* считывание полного имени из канала */
9 mesg.mesg_type = 1;
10 if ((n = Mesg_recv(readfd, &mesg)) == 0)
11 err_quit("pathname missing");
12 mesg.mesg_data[n] = '\0'; /* полное имя, завершающееся 0 */
13 if ((fp = fopen(mesg.mesg_data, "r")) == NULL) {
14 /* ошибка, нужно сообщить клиенту */
15 snprintf(mesg.mesg_data + n, sizeof(mesg.mesg_data) – n,