Вам нужно разрешить множественным клиентским процессам отправлять данные серверу. Для простоты предположим, что данные, которые нужно обработать, можно разбить на блоки, каждый из которых меньше PIPE_BUF
байтов. Конечно, реализовать такую систему можно разными способами, но мы рассмотрим только один, как иллюстрацию применения именованных каналов.
Поскольку сервер будет обрабатывать только один блок данных в каждый момент времени, кажется логичным создать один канал FIFO, который читается сервером и в который записывают всё клиенты. Если открыть FIFO в блокирующем режиме, сервер и клиенты будут при необходимости блокироваться.
Возвращать обработанные данные клиентам немного сложнее. Вам придется организовать второй канал для возвращаемых данных, один для каждого клиента. Если передавать идентификатор (PID) процесса-клиента в исходных данных, отправляемых на сервер, обе стороны смогут использовать его для генерации уникального имени канала с возвращаемыми данными.
Выполните упражнение 13.13.
Упражнение 13.13. Пример клиент-серверного приложения
1. Прежде всего, вам нужен заголовочный файл client.h, в котором определены данные, общие для серверных и клиентских программ. В приложение также для удобства включены требуемые системные заголовочные файлы.
#include
#include
#include
#include
#include
#include
#include
#include
#define SERVER_FIFO_NAME "/tmp/serv_fifo"
#define CLIENT_FIFO_NAME "/tmp/cli_%d_fifo"
#define BUFFER_SIZE 20
struct data_to_pass_st {
pid_t client_pid;
char some_data[BUFFER_SIZE - 1];
};
2. Теперь займемся серверной программой server.c. В этом разделе вы создаете и затем открываете канал сервера. Он задается в режиме "только для чтения" и с блокировкой. После засыпания (из демонстрационных соображений) сервер читает данные от клиента, у которого есть структура типа data_to_pass_st
.
#include "client.h"
#include
int main() {
int server_fifo_fd, client fifo_fd;
struct data_to_pass_st my_data;
int read_res;
char client_fifo[256];
char *tmp_char_ptr;
mkfifo(SERVER_FIFO_NAME, 0777);
server_fifo_fd = open(SERVER_FIFO_NAME, O_RDONLY);
if (server_fifo_fd == -1) {
fprintf(stderr, "Server fifo failure\n");
exit(EXIT_FAILURE);
}
sleep(10); /* для целей демонстрации разрешает клиентам создать очередь */
do {
read_res = read(server_fifo_fd, &my_data, sizeof(my_data));
if (read res > 0) {
3. На следующем этапе вы выполняете некоторую обработку данных, только что полученных от клиента: преобразуете все символы в некоторых данных в прописные и соединяете CLIENT_FIFO_NAME
с полученным идентификатором client_pid
.
tmp_char_ptr = my_data.some_data;
while (*tmp_char_ptr) {
*tmp_char_ptr = toupper(* tmp_char_ptr);
tmp_char_ptr++;
}
sprintf(client_fifo, CLIENT_FIFO_NAME, my_data.client_pid);
4. Далее отправьте обработанные данные назад, открыв канал клиентской программы в режиме "только для записи" и с блокировкой. В заключение закройте серверный FIFO с помощью закрытия файла и отсоединения FIFO.
client_fifo_fd = open(client_fifo, O_WRONLY);
if (client_fifo_fd ! = -1) {
write(client_fifo_fd, &my_data, sizeof(my_data));
close(client_fifo_fd);
}
}
} while (read_res > 0);
close(server_fifo_fd);
unlink(SERVER_FIFO_NAME);
exit(EXIT_SUCCESS);
}
5. Далее приведена клиентская программа client.с. В первой части этой программы FIFO сервера, если он уже существует, открывается как файл. Далее программа получает идентификатор собственного процесса, который формирует некие данные, которые будут отправляться на сервер. Создается FIFO клиента, подготовленный для следующего раздела.
#include "client.h"
#include
int main() {
int server_fifo_fd, client_fifo_fd;
struct data_to_pass_st my_data;
int times_to_send;
char client_fifo[256];
server_fifo_fd = open(SERVER_FIFO_NAME, O_WRONLY);
if (server_fifo_fd == -1) {
fprintf (stderr, "Sorry, no server\n");
exit(EXIT_FAILURE);
}
my_data.client_pid = getpid();
Читать дальше