- •Введение
- •Глава 1. Фундаментальные концепции unix Систем
- •Программы, процессы и потоки
- •Сигналы
- •Идентификаторы процессов, группы процессов и сеансы
- •Система прав
- •Другие атрибуты процесса
- •Межпроцессное взаимодействие
- •Использование системных вызовов
- •Краткие описания функций и обработка ошибок
- •Контрольные вопросы
- •Литература
- •Глава 2. Базовые операции ввода-вывода
- •Файловые операции ввода - вывода
- •Стандартные дескрипторы
- •Системные вызовы open и creat
- •Системный вызов umask
- •Системный вызов unlink
- •Текущая позиция в файле
- •Системный вызов write
- •2.8. Системный вызов read
- •2.9. Системный вызов close
- •2.10. Системный вызов lseek
- •2.11. Системные вызовы pread и pwrite
- •2.12. Системные вызовы truncate и ftruncate
- •Контрольные вопросы
- •Литература
- •Глава 3. Дополнительные операции файлового ввода_вывода
- •Низкоуровневый доступ к файловой системе
- •Жесткие и символические ссылки
- •Системный вызов getcwd
- •Отображение метаданных файла
- •Системные вызовы getpwuid, getgrgid и getlogin
- •Каталоги
- •Системные вызовы chdir и fchdir
- •Системные вызовы mkdir и rmdir
- •Контрольные вопросы
- •Литература
- •Глава 4. Процессы и потоки
- •4.1. Среда окружения
- •Системный вызов exec
- •Системный вызов fork
- •Завершение процесса и системные вызовы exit
- •Системные вызовы wait, waitpid и waitid
- •Получение и изменение идентификаторов пользователя и группы
- •Получение и изменение приоритета
- •Контрольные вопросы
- •Литература
- •Глава 5. Механизмы межпроцессного взаимодействия
- •5.1. Каналы
- •5.2. Системные вызовы dup и dup2
- •5.3. Двунаправленное взаимодействие с использованием однонаправленных каналов
- •Контрольные вопросы
- •Литература
- •Глава 6.Механизмы взаимодействия процессов
- •Именованные каналы (fifo)
- •Системные вызовы для работы с очередями сообщений posix
- •Семафоры
- •Системные вызовы для работы с общей памятью posix
- •Контрольные вопросы
- •Литература
- •Глава 7.Сетевое взаимодействие и сокеты
- •Основные системные вызовы для работы с сокетами, образующими логические соединения
- •Обслуживание нескольких клиентов
- •Адресация сокетов
- •In_port_t sin_port; /* номер порта (uint16_t) */
- •In_addr_t s_addr; /* адрес iPv4 (uint32_t) */
- •Домен адресов af_inet6
- •In_port_t sin6_port; /* номер порта (uint16_t) */
- •Доменная система именования
- •Параметры сокетов
- •Контрольные вопросы
- •Литература
- •Глава 8.Сигналы и таймеры
- •Введение в сигналы
- •Жизненный цикл сигналов
- •Типы сигналов
- •Системный вызов sigaction
- •Контрольные вопросы
- •Литература
- •Заключение
- •Список литературы
- •Глава 2. Базовые операции ввода-вывода 14
- •Глава 3. Дополнительные операции файлового ввода_вывода 25
- •Глава 6. Механизмы взаимодействия процессов 58
Обслуживание нескольких клиентов
Усовершенствуем предыдущий пример так, чтобы сервер мог обслуживать несколько клиентов одновременно. Серверная часть программы выглядит следующим образом:
static bool run_server(struct sockaddr_un *sap)
{
int fd_skt, fd_client, fd_hwm = 0, fd;
char buf[100];
fd_set set, read_set;
ssize_t nread;
fd_skt = socket(AF_UNIX, SOCK_STREAM, 0);
bind(fd_skt, (struct sockaddr *)sap, sizeof(*sap));
listen(fd_skt,SOMAXCONN);
if (fd_skt > fd_hwm)
fd_hwm = fd_skt;
FD_ZERO(&set);
FD_SET(fd_skt, &set);
while (true) {
read_set = set;
select(fd_hwm + 1, &read_set, NULL, NULL, NULL);
for (fd = 0; fd <= fd_hwm; fd++)
if (FD_ISSET(fd, &read_set)) {
if (fd == fd_skt) {
fd_client = accept(fd_skt, NULL, 0);
FD_SET(fd_client, &set);
if (fd_client > fd_hwm)
fd_hwm = fd_client;
}
else {
nread = read(fd, buf, sizeof(buf));
if (nread == 0) {
FD_CLR(fd, &set);
if (fd == fd_hwm)
fd_hwm-;
close(fd);
}
else {
printf(“Сервер получил сообщение \”%s\”\n”, buf);
write(fd, “Пока!”, 9);
}
}
}
}
close(fd_skt);
return true;
EC_CLEANUP_BGN
return false;
EC_CLEANUP_END
}
В функции run_server определено два набора дескрипторов. Первый, с именем set, хранит все файловые дескрипторы сокетов, сокет сервера, который принимает запросы на соединение, и сокеты для связи с клиентами, созданные вызовом accept. Второй набор дескрипторов, с именем red_set, копируется из набора set перед каждым обращением к select. После возврата из select выполняется поиск дескриптора, готового для выполнения операции ввода-вывода. Если готовым оказался сокет fd_skt, выполняется обращение к вызову accept. Если какой-либо другой, то производится прием данных, поступивших от клиента, и в ответ отправляется короткое сообщение.
Рассмотрим исходный код функции, реализующий клиентскую часть программы:
static bool run_client(struct sockaddr_un *sap)
{
if (fork() ==0) {
int fd_skt;
char buf[100];
fd_skt = socket(AF_UNIX, SOCK_STREAM, 0);
while (connect(fd_skt, (struct sockaddr *)sap, sizeof(*sap)) == -1)
if (errno ==ENOENT) {
sleep(1);
continue;
}
else
EC_FAIL
snprintf(buf, sizeof(buf), “Привет от клиента %ld”, (long)getpid());
write(fd_skt, buf, strlen(buf) +1);
read(fd_skt, buf, sizeof(buf));
printf( “Клиент получил сообщение \”%s\”\n”, buf);
close(fd_skt);
exit(EXIT_SUCCESS);
}
return true;
EC_CLEANUP_BGN
exit(EXIT_FAILURE);
EC_CLEANUP_END
}
Рассмотрим функцию main, которая назначает адрес сокета, создает четыре дочерних процесса и запускает сервер в родительском процессе:
#define SOCKETNAME “MySocket”
{
struct sockaddr_un sa;
int nclient;
(void)unlink(SOCKETNAME);
strcpy(sa, sun_path, SOCKETNAME);
sa.sun_family = AF_UNIX;
for (nclient = 1; nclient <= 4; nclient++)
run_client(&sa);
run_server(&sa);
exit(EXIT_SUCCESS);
EC_CLEANUP_BGN
exit(EXIT_FAILURE);
EC_CLEANUP_END
}
В результате работы программы было получено следующее:
Сервер получил сообщение “Привет от клиента 31786”
Клиент получил сообщение “Пока!”
Сервер получил сообщение “Привет от клиента 31785”
Клиент получил сообщение “Пока!”
Сервер получил сообщение “Привет от клиента 31784”
Клиент получил сообщение “Пока!”
Сервер получил сообщение “Привет от клиента 31787”
Клиент получил сообщение “Пока!”