char mtext[1]; /* содержание сообщения */
};
size_t msgsz – размер сообщения;
int msgflg – флаги, описывающие режим работы функции, например, вызов функции без блокировки (IPC_NOWAIT).
Прием сообщений из очереди производится вызовом:
ssize_t msgrcv(int msqid, struct msgbuf *msgp, size_t msgsz, long msgtyp, int msgflg);
где:
int msqid – идентификатор очереди;
struct msgbuf *msgp – буфер для приема сообщений;
size_t msgsz – размер сообщения;
long msgtyp – тип сообщения;
int msgflg – флаги, описывающие режим работы функции.
После работы с очередью ее необходимо удалить вызовом:
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
где:
int msqid – идентификатор очереди;
int cmd – команда управления, для удаления IPC_RMID;
struct msqid_ds *buf – буфер для установки и получения информации об очереди, игнорируется в случае команды удаления.
Написать комплект из двух программ, одна из которых передает сообщение в очередь сообщений, а вторая – принимает сообщения из очереди сообщений. Проверить работу функций с блокировкой и без блокировки.
Студент, который находится в списке группы под нечетным номером, реализует очередь по стандарту SVID.
Студент, который находится в списке группы под четным номером, реализует очередь по стандарту POSIX.
Шаблон программы 1 представлен ниже:
объявить флаг завершения потока;
объявить идентификатор очереди сообщений;
Функция потока()
{
объявить буфер;
пока (флаг завершения потока не установлен)
{
сформировать сообщение в буфере;
записать сообщение из буфера в очередь сообщений;
задержать на время;
}
}
основная программа()
{
объявить идентификатор потока;
создать (или открыть, если существует) очередь сообщений;
создать поток из функции потока;
ждать нажатия клавиши;
установить флаг завершения потока;
ждать завершения потока;
закрыть очередь сообщений;
удалить очередь сообщений;
}
Шаблон программы 2 представлен ниже:
объявить флаг завершения потока;
объявить идентификатор очереди сообщений;
Функция потока()
{
объявить буфер;
пока (флаг завершения потока не установлен)
{
очистить буфер сообщения;
принять сообщение из очереди сообщений в буфер;
вывести сообщение на экран;
}
}
основная программа()
{
объявить идентификатор потока;
создать (или открыть, если существует) очередь сообщений;
создать поток из функции потока;
ждать нажатия клавиши;
установить флаг завершения потока;
ждать завершения потока;
закрыть очередь сообщений;
удалить очередь сообщений;
}
Какие программные интерфейсы для работы с очередями сообщений существуют?
Дайте сравнительную характеристику программных интерфейсов очередей сообщений.
Каким образом обеспечить проверку наличия сообщений в очереди без блокирования процессов?
Каким образом обеспечить проверку наличия сообщений в очереди с определенной периодичностью?
Как осуществить передачу и прием оповещения от очереди о появлении нового сообщения в очереди?
Каким образом можно менять размер сообщений и количество сообщений в очереди?
Цель работы - знакомство студентов с механизмом взаимодействия удаленных процессов – сокетами и с системными вызовами, обеспечивающими установление соединения, разъединение, а также передачу и прием данных.
Сокеты представляют собой программный интерфейс, который предоставляется операционной системой для взаимодействия удаленных процессов.
В зависимости от выбираемых параметров сокеты могут поддерживать локальные соединения, протоколы Интернет, протоколы Novell, Х.25 и другие.
Сокеты поддерживают обмен сообщениями с установлением соединения (протокол ТСР), обеспечивающий надежную упорядоченную передачу сообщений, и обмен сообщениями без установления соединения (протокол UDP), обеспечивающий ненадежную передачу сообщений, которые могут теряться, и порядок поступления которых может быть нарушен.
Сокет создается следующим вызовом:
int socket(int domain, int type, int protocol),
где:
domain. – определяет тип коммуникационного протокола (Интернет, Novell, Х.25);
type – определяет тип передачи (надежная, ненадежная);
protocol – конкретизация типа коммуникационного протокола.
К сокету, который на сервере будет выполнять функцию прослушивания, привязывается адрес следующим вызовом:
int bind(int s,
struct sockaddr *addr,
socklen_t addrlen),
где:
s – дескриптор сокета;
addr – указатель на структуру, содержащую адрес, к которому привязывается сокет;
addrlen – размер структуры.
Перевод сокета в состояние прослушивания осуществляется вызовом:
int listen(int s, int backlog),
где:
s – дескриптор сокета;
backlog – размер очереди соединений с клиентами.
Прием первого соединения из очереди соединений с клиентами осуществляется вызовом:
int accept(int s,
struct sockaddr *addr,
socklen_t *addrlen),
где:
s – дескриптор слушающего сокета;
addr – указатель на структуру, содержащую адрес клиента;
addrlen – размер структуры.
Установление соединения с сервером осуществляется вызовом:
int connect(int s,
const struct sockaddr *addr,
socklen_t addrlen),
где:
s – дескриптор сокета;
addr – указатель на структуру, содержащую адрес сервера;
addrlen – размер структуры.
Передача данных в сокет осуществляется следующим вызовом:
int send(int s, const void *msg, size_t len, int flags),
где:
s – дескриптор сокета;
msg – адрес буфера, содержащего данные для передачи;
len – размер передаваемых данных;
flags – флаги, описывающие особенности передачи.
Прием данных из сокета производится вызовом:
int recv(int s, void *buf, size_t len, int flags),
где:
s – дескриптор сокета;
buf - адрес буфера, в который принимаются данные;
len – размер буфера;
flags – флаги, описывающие особенности приема.
Чтобы закрыть соединение, необходимо вызвать функцию, которая запрещает прием или передачу (или и то и другое) через сокет:
int shutdown(int socket, int how);
где:
int socket – дескриптор сокета;
int how – способ запрета (прием, передача, прием и передача).
Для передачи данных без установления соединения вызовы функций listen(), accept() и connect() не требуются.
Передача сообщений без установления соединения осуществляется вызовом:
ssize_t sendto(int s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);
где:
int s – дескриптор сокета;
const void *msg – указатель на буфер сообщения;
size_t len – длина сообщения;
int flags – битовая маска, описывающая режим работы функции;
const struct sockaddr *to – структура, описывающая получателя сообщения;
socklen_t tolen – размер структуры.
Прием сообщений без установления соединения осуществляется вызовом:
int recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);
где:
int s – дескриптор сокета;
void *buf – указатель на буфер сообщения;
size_t len – размер буфера;
int flags – битовая маска, описывающая режим работы функции;
const struct sockaddr *from – структура, описывающая отправителя сообщения;
socklen_t *fromlen – указатель на переменную, содержащую размер структуры.
Перед завершением работы сокет необходимо закрыть вызовом функции:
int close(int fd);
где:
int fd – дескриптор сокета.
Написать комплект из двух программ, одна из которых выполняет функции сервера, а вторая выполняет функции клиента.
Клиент после установления соединения с сервером посылает ему запросы.
Сервер принимает запросы, обрабатывает их и отправляет ответы клиенту. Функцию обработки следует выбрать из таблицы.
Клиент принимает ответы и выводит их на экран.
Использовать функции работы с сокетами без блокировки.
В качестве очередей запросов на обработку и передачу использовать типы данных std::vector или std::queue.
Необходимо учесть, что очереди запросов на обработку и передачу являются критическими ресурсами, с которыми работают несколько потоков одновременно. Поэтому работа с очередями должна производиться в режиме взаимного исключения.
Студент, который находится в списке группы под четным номером, использует протокол с установлением соединения (TCP).
Студент, который находится в списке группы под нечетным номером, использует протокол без установления соединения (UDP).
Шаблон программы-сервера для случая TCP представлен ниже:
объявить идентификатор «слушающего» сокета;
объявить идентификатор сокета для работы с клиентом;
объявить идентификатор очереди запросов на обработку;
объявить идентификатор очереди ответов на передачу;
объявить флаг завершения потока приема запросов;
объявить флаг завершения потока обработки запросов;
объявить флаг завершения потока передачи ответов;
объявить флаг завершения потока ожидания соединений;
функция приема запросов()
{
пока (флаг завершения потока приема не установлен)
{
принять запрос из сокета;
положить запрос в очередь на обработку;
}
}
функция обработки запросов()
{
пока (флаг завершения потока обработки не установлен)
{
прочитать запрос из очереди на обработку;
обработать запрос и сформировать ответ;
положить ответ в очередь на передачу;
}
}
функция передачи ответов()
{
пока (флаг завершения потока передачи не установлен)
{
прочитать ответ из очереди на передачу;
передать ответ в сокет;
}
}
функция ожидания соединений()
{
пока (флаг завершения потока ожидания соединений не установлен)
{
прием соединения от клиента;
если соединение принято
{
создать поток приема запросов;
создать поток обработки запросов;
создать поток передачи ответов;
завершить работу потока ожидания соединений;
}
}
}
основная программа()
{
объявить идентификатор потока приема запросов;
объявить идентификатор потока обработки запросов;
объявить идентификатор потока передачи ответов;
объявить идентификатор потока ожидания соединений;
создать «слушающий» сокет;
привязать «слушающий» сокет к адресу;
перевести сокет в состояние прослушивания;
создать очередь запросов на обработку;
создать очередь ответов на передачу;
создать поток ожидания соединений;
ждать нажатия клавиши;
установить флаг завершения потока приема запросов;
установить флаг завершения потока обработки запросов;
установить флаг завершения потока передачи ответов;
установить флаг завершения потока ожидания соединений;
ждать завершения потока приема запросов;
ждать завершения потока обработки запросов;
ждать завершения потока передачи ответов;
ждать завершения потока ожидания соединений;