Скачиваний:
85
Добавлен:
12.05.2015
Размер:
913.92 Кб
Скачать

18.6. Пример программы клиент-сервер

Перепишем наш пример программы типа клиент-сервер из раздела 13.2 с использованием двух очередей сообщений. Одна из очередей предназначена для передачи сообщений от клиента серверу, а другая – в обратную сторону.

Программный код сервера приведен в листинге 17.2. Программа создает обе очереди сообщений, и не беда, если какая-нибудь из них уже существует, потому что мы не указываем флагIPC_EXCL. Далее программа вызывает функциюserver. По окончании передачи данных сервер отправляет сообщение, содержащее только заголовок (без данных) для обозначения конца связи.

Листинг 17.2. Программа-сервер, использующая очереди сообщений

#include <errno.h>

#include <stdio.h>

#include <stdlib.h>

#include <sys/stat.h>

#include <sys/msg.h>

#define MSG_R S_IRUSR

#define MSG_W S_IWUSR

#define SVMSG_MODE (MSG_R | MSG_W | MSG_R>>3 | MSG_R>>6)

#define MQ_KEY1 1234L

#define MQ_KEY2 2345L

#define MAXMESGDATA (4096 - 2*sizeof(long))

struct mymesg

{

long mesg_type; /* тип сообщения, должен быть > 0 */

long mesg_len; /* количество байтов в массиве mesg_data, может быть 0 */

char mesg_data[MAXMESGDATA];

};

void server(int readid, int writeid)

{

FILE *fp;

ssize_t n;

struct mymesg mesg;

char buff[MAXMESGDATA];

/* чтение полного имени файла из канала IPC */

if ((n = msgrcv(readid, &mesg, sizeof(mesg.mesg_len) + MAXMESGDATA, 1, 0))

== -1)

{

fprintf(stderr, "Сервер: ошибка чтения полного имени файла из канала IPC:

%s\n", strerror(errno));

exit(1);

}

if (n == sizeof(mesg.mesg_len))

{

fprintf(stderr, "Сервер: получено пустое имя файла, завершение работы\n");

exit(1);

} /* добавляем признак конца строки к полному имени */

mesg.mesg_data[n - sizeof(mesg.mesg_len)] = '\0';

strncpy(buff, mesg.mesg_data, sizeof(mesg.mesg_data));

mesg.mesg_type = 1;

if ((fp = fopen(buff, "r")) == NULL)

{ /* сообщаем клиенту об ошибке открытия файла */

snprintf(mesg.mesg_data, sizeof(mesg.mesg_data), "Сервер: ошибка открытия

файла %s: %s\n", buff, strerror(errno));

mesg.mesg_len = strlen(mesg.mesg_data);

if (msgsnd(writeid, &mesg, sizeof(mesg.mesg_len) + strlen(mesg.mesg_data),

0) == -1)

{

fprintf(stderr, "Сервер: ошибка записи в канал IPC: %s\n",

strerror(errno));

exit(1);

}

}

else /* файл успешно открыт; копируем его в канал IPC */

{

while (fgets(mesg.mesg_data, MAXMESGDATA, fp) != NULL)

{

mesg.mesg_len = strlen(mesg.mesg_data);

if (msgsnd(writeid, &mesg, sizeof(mesg.mesg_len) + strlen(mesg.mesg_data),

0) == -1)

{

fprintf(stderr, "Сервер: ошибка записи содержимого файла %s в канал IPC:

%s\n", buff, strerror(errno));

exit(1);

}

}

if (ferror(fp))

{

snprintf(mesg.mesg_data, sizeof(mesg.mesg_data), "Сервер: ошибка чтения

содержимого файла %s: %s\n", buff, strerror(errno));

mesg.mesg_len = strlen(mesg.mesg_data);

if (msgsnd(writeid, &mesg, sizeof(mesg.mesg_len) + strlen(mesg.mesg_data),

0) == -1)

{

fprintf(stderr, "Сервер: ошибка записи в канал IPC: %s\n",

strerror(errno));

exit(1);

}

}

fclose(fp);

}

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

mesg.mesg_len = 0;

if (msgsnd(writeid, &mesg, sizeof(mesg.mesg_len), 0) == -1)

{

fprintf(stderr, "Сервер: ошибка отправки признака конца связи по каналу IPC:

%s\n", strerror(errno));

exit(1);

}

}

int main(int argc, char **argv)

{

int readid, writeid;

if ((readid = msgget(MQ_KEY1, SVMSG_MODE | IPC_CREAT)) == -1)

{

fprintf(stderr, "Сервер: ошибка вызова функции msgget(%lu): %s\n", MQ_KEY1,

strerror(errno));

exit(1);

}

if ((writeid = msgget(MQ_KEY2, SVMSG_MODE | IPC_CREAT)) == -1)

{

fprintf(stderr, "Сервер: ошибка вызова функции msgget(%lu): %s\n", MQ_KEY2,

strerror(errno));

exit(1);

}

server(readid, writeid);

exit(0);

}

В листинге 17.3приведен программный код клиента. Программа открывает две очереди сообщений и вызывает функциюclient.

Листинг 17.3. Программа-клиент, использующая очереди сообщений

#include <errno.h>

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/msg.h>

#define MQ_KEY1 1234L

#define MQ_KEY2 2345L

#define MAXMESGDATA (4096 - 2*sizeof(long))

struct mymesg

{

long mesg_type; /* тип сообщения, должен быть > 0 */

long mesg_len; /* количество байтов в массиве mesg_data, может быть 0 */

char mesg_data[MAXMESGDATA];

};

int readid, writeid;

void msg_del(void)

{

/* удаление очередей сообщений */

if (msgctl(writeid, IPC_RMID, NULL) == -1)

fprintf(stderr, "Клиент: ошибка вызова функции msgctl(%lu, IPC_RMID): %s\n",

MQ_KEY1, strerror(errno));

if (msgctl(readid, IPC_RMID, NULL) == -1)

fprintf(stderr, "Клиент: ошибка вызова функции msgctl(%lu, IPC_RMID): %s\n",

MQ_KEY2, strerror(errno));

}

void client(int readid, int writeid)

{

size_t len;

ssize_t n;

struct mymesg mesg;

/* чтение полного имени файла из стандартного потока ввода */

if (fgets(mesg.mesg_data, MAXMESGDATA, stdin) == NULL)

{

fprintf(stderr, "Клиент: ошибка чтения полного имени файла из стандартного

потока ввода\n");

exit(1);

} /* fgets() гарантирует завершающий нулевой байт */

len = strlen(mesg.mesg_data);

if (mesg.mesg_data[len-1] == '\n')

len--; /* удаление символа перевода строки (если есть) */

if (len == 0)

{

fprintf(stderr, "Клиент: введено пустое имя файла, завершение работы\n");

exit(1);

}

mesg.mesg_type = 1;

mesg.mesg_len = len;

/* запись полного имени файла в канал IPC */

if (msgsnd(writeid, &mesg, sizeof(mesg.mesg_len) + len, 0) == -1)

{

fprintf(stderr, "Клиент: ошибка записи полного имени файла в канал IPC:

%s\n", strerror(errno));

exit(1);

}

/* считывание из канала IPC, запись в стандартный поток вывода */

while ((n = msgrcv(readid, &mesg, sizeof(mesg.mesg_len) + MAXMESGDATA, 1, 0))

> 0)

{

if (n == sizeof(mesg.mesg_len))

break; /* принято сообщение без данных; конец связи */

if (write(STDOUT_FILENO, mesg.mesg_data, n - sizeof(mesg.mesg_len)) !=

n - sizeof(mesg.mesg_len))

{

fprintf(stderr, "Клиент: ошибка записи содержимого файла в стандартный поток вывода: %s\n", strerror(errno));

exit(1);

}

}

if (n == -1)

{

fprintf(stderr, "Клиент: ошибка чтения содержимого файла из канала IPC:

%s\n", strerror(errno));

exit(1);

}

}

int main(int argc, char **argv)

{

if (atexit(msg_del))

{

fprintf(stderr, "Клиент: невозможно зарегистрировать msg_del: %s\n",

strerror(errno));

exit(1);

}

/* очереди сообщений должны быть созданы сервером */

if ((writeid = msgget(MQ_KEY1, 0)) == -1)

{

fprintf(stderr, "Клиент: ошибка вызова функции msgget(%lu): %s\n", MQ_KEY1,

strerror(errno));

exit(1);

}

if ((readid = msgget(MQ_KEY2, 0)) == -1)

{

fprintf(stderr, "Клиент: ошибка вызова функции msgget(%lu): %s\n", MQ_KEY2,

strerror(errno));

exit(1);

}

client(readid, writeid);

exit(0);

}

Соседние файлы в папке Chapter.4