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

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

8   /* считывание заголовка сообщения для определения его длины */

9   if ((n = Read(fd, mptr, MESGHDRSIZE)) == 0)

10   return(0); /* end of file */

11  else if (n != MESGHDRSIZE)

12   err_quit("message header: expected %d, got %d". MESGHDRSIZE, n);

13  if ((len = mptr->mesg_len) > 0)

14   if ((n = Read(fd, mptr->mesg_data, len)) != len)

15    err_quit("message data: expected %d, got %d", len, n);

16  return(len);

17 }

Теперь для каждого сообщения функция 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,

16    ": can't open, %s\n", strerror(errno));

17   mesg.mesg_len = strlen(mesg.mesg_data);

18   Mesg_send(writefd, &mesg);

19  } else {