Лабораторная работа №3 «Обмен сообщениями» — КиберПедия 

Особенности сооружения опор в сложных условиях: Сооружение ВЛ в районах с суровыми климатическими и тяжелыми геологическими условиями...

Типы оградительных сооружений в морском порту: По расположению оградительных сооружений в плане различают волноломы, обе оконечности...

Лабораторная работа №3 «Обмен сообщениями»

2021-05-27 44
Лабораторная работа №3 «Обмен сообщениями» 0.00 из 5.00 0 оценок
Заказать работу

2.3.1 Теория

Архитектура и структура обмена сообщениями

 

Три ключевые выражения:

· «Клиент посылает (sends) сообщение серверу»;

· «Сервер принимает (receives) сообщение от клиента»;

· «Сервер отвечает (replies) клиенту».

Эти выражения в точности соответствуют действительным именам функций, которые используются для передачи сообщений в QNX/Neutrino.

Минимальный полезный набор функций включает в себя функции ChannelCreateQ, ConnectAttach(), MsgReplyQ, MsgSend() и MsgRecievef).

Разобьем обсуждение на две части: отдельно обсудим функции, которые применяются на стороне клиента, и отдельно — те, что применяются на стороне сервера.

Клиент

 

Клиент, который желает послать запрос серверу, блокируется до тех пор, пока сервер не завершит обработку запроса. Затем, после завершения сервером обработки, запроса клиент разблокируется, чтобы принять «ответ».

Это подразумевает обеспечение двух условий: клиент должен «уметь» сначала установить соединение с сервером, а потом обмениваться с ним данными с помощью сообщений — как в одну сторону (запрос — «send»), так и в другую (ответ — «reply»).

Установление соединения

 

Первое, что должны сделать — это установить соединение с помощью функции ConnectAttach(), описанной следующим образом:

 

#include <sys/neutrino.h>

int ConnectAttach

(int nd,pid_t pid, int chid, unsigned index, int flags);

 

Функции ConnectAttach() передаются три идентификатора;

· nd— дескриптор узла (Node Descriptor),

· pid— идентификатор процесса (process ID)

· chid — идентификатор канала (channel ID).

Вместе эти три идентификатора, которые обычно записываются в виде «ND/PID/CHID», однозначно идентифицируют сервер, с которым клиент желает соединиться. Аргументы index и flags мы здесь просто проигнорируем (установим их в ноль).

Итак, предположим, что мы хотим подсоединиться к процессу, находящемуся на нашем узле и имеющему идентификатор 77, по каналу с идентификатором 1. Ниже приведен пример программы для выполнения этого:

 

int coid;

coid = ConnectAttach (0, 77 „ 1, 0, 0);

 

Можно видеть, что присвоением идентификатору узла (nd) нулевого значения мы сообщаем ядру о том, что мы желаем установить соединение на локальном узле.

Соединиться надо с процессом 77 и по каналу 1.

С этого момента есть идентификатор соединения — небольшое целое число, которое однозначно идентифицирует соединение моего клиента с конкретным сервером по заданному каналу.

Теперь можно применять этот идентификатор для отправки запросов серверу сколько угодно раз. Выполнив все, для чего предназначалось соединение, его можно уничтожить с помощью функции:

ConnectDetach (coid);

Передача сообщений (sending)

 

Передача сообщения со стороны клиента осуществляется применением функции MsgSend(). Мы рассмотрим это на примере:

 

#include <sys/neutrino.h>

int MsgSend (int coid,

const void *smsg, int sbytes, void *rmsg, int rbytes);

 

Аргументами функции MsgSendQ являются:

· идентификатор соединения с целевым сервером (coid);

· указатель на передаваемое сообщение (smsg);

· размер передаваемого сообщения (sbytes);

· указатель на буфер для ответного сообщения (rmsg);

· размер ответного сообщения (rbytes).

 

Передадим сообщение процессу с идентификатором 77 по каналу 1:

 

#include <sys/neutrino.h>

char *smsg = «Это буфер вывода»; char rmsg [200]; int coid;

// Установить соединение

coid = ConnectAttach (0, 77, 1, 0, 0);

if (coid == -1) {

fprintf (stderr, «Ошибка ConnectAttach к 0/77/1!\n»);

perror (NULL);

exit (EXIT_FAILURE);

 

// Послать сообщение

if(MsgSend(coid, smsg,strlen (smsg) + 1,rmsg,sizeof(rmsg)) == -1)

{ fprintf (stderr, «Ошибка MsgSendXn»);

 perror (NULL);

exit (EXIT_FAILURE);

if (strlen (rmsg) > 0)

 {

printf («Процесс с ID 77 возвратил \«%s\»\n», rmsg);

}

 

Предположим, что процесс с идентификатором 77 был действительно активным сервером, ожидающим сообщение именно такого формата по каналу с идентификатором 1. После приема сообщения сервер обрабатывает его и в некоторый момент времени выдает ответ с результатами обработки. В этот момент функция MsgSendQ должна возвратить ноль (0), указывая этим, что все прошло успешно. Если бы сервер послал нам в ответ какие-то данные, мы смогли бы вывести их на экран с помощью последней строки в программе.

Сервер. Создание канала

 

Сервер должен создать канал — то, к чему присоединялся клиент, когда вызывал функцию ConnectAttach(). Обычно сервер, однажды создав канал, приберегает его «впрок».

Канал создается с помощью функции ChannelCreate() и уничтожается с помощью функции ChannelDestroyQ:

 

#include <sys/neutrino.h>

int ChannelCreate (unsigned flags);

int ChannelDestroy (int chid);

 

Таким образом, для создания канала сервер должен сделать так:

int chid;

chid = ChannelCreate (0);

 

Теперь у нас есть канал. В этом пункте клиенты могут подсоединиться (с помощью функции ConnectAttachQ) к этому каналу и начать передачу сообщений:

Рисунок 3 Связь между каналом сервера и клиентским соединением.

 

Обработка сообщений

 

В терминах обмена сообщениями, сервер отрабатывает схему обмена
в два этапа:

 

· этап «приема» (receive);

· этап «ответа» (reply).

Рисунок 4 Взаимосвязь функций клиента и сервера при обмене сообщениями.

Обсудим два простейших варианта соответствующих функций, MsgReceiveQ и MsgReply().

 

#include <sys/neutrino.h>

int MsgReceive (int chid,void *rmsg, int rbytes, struct _msg_info *info);

int MsgReply (int rcvid, int status, const void *msg, int nbytes);

 

Четыре основных элемента:

 

1 Клиент вызывает функцию MsgSend() и указывает ей на буфер передачи (указателем smsg и длиной sbytes). Данные передаются в буфер функции MsgReceiveQ на стороне сервера, по адресу rmsg и длиной rbytes. Клиент блокируется.

2 Функция MsgReceiveQ сервера разблокируется и возвращает идентификатор отправителя rcvid, который будет впоследствии использован для ответа. Теперь сервер может использовать полученные от клиента данные.

3 Сервер завершил обработку сообщения и теперь использует идентификатор отправителя rcvid, полученный от функции MsgReceiveQ, передавая его функции MsgReply(). Заметьте, что местоположение данных для передачи функции MsgReply() задается как указатель на буфер (smsg) определенного размера (sbytes). Ядро передает данные клиенту.

4 Наконец, ядро передает параметр sts, который используется функцией MsgSend() клиента как возвращаемое значение. После этого клиент разблокируется.

 

Для каждой буферной передачи указываются два размера (в случае запроса от клиента клиента это sbytes на стороне клиента и rbytes на стороне сервера; в случае ответа сервера это sbytes на стороне сервера и rbytes на стороне клиента). Это сделано для того, чтобы разработчики каждого компонента смогли определить размеры своих буферов — из соображений дополнительной безопасности.

2.3.2 Текст программы

// server.c

#include <stdio.h>

#include <pthread.h>

#include <inttypes.h>

#include <errno.h>

#include <sys/neutrino.h>

 

void server(void)

{

int rcvid;    //Ykazivaet komy nado otvechat

int chid;     //Identifikator kanala

char message[512]; //

 

printf("Server start working \n");

 

 chid=ChannelCreate(0);    //Sozdanie Kanala

 printf("Chanel id: %d \n", chid);

 printf("Pid: %d \n", getpid());

 // vipolniaetsa vechno- dlia servera eto normalno

 while(1)

{

// Polychit i vivesti soobshenie

 

rcvid=MsgReceive(chid,message,sizeof(message), NULL);

printf("Polychili soobshenie, rcvid %X \n",rcvid);

printf("Soobshenie takoe: \"%s\". \n", message);

   

// Podgotovit otvet

   

strcpy(message,"Eto otvet");

   

MsgReply(rcvid, EOK, message, sizeof(message));

printf("\"%s\". \n", message);

}

 

int main(void)

{

printf("Prog server \n");

server();

sleep(5);

return(1);

}

 

//client.c

#include <stdio.h>

#include <pthread.h>

#include <inttypes.h>

#include <errno.h>

#include <sys/neutrino.h>

 

int main(void)

{

char smsg[20];

char rmsg[200];

int coid;

long serv_pid;

printf("Prog client, Vvedite PID servera \n");

scanf ("%ld",&serv_pid);

printf("Vveli %ld \n", serv_pid);

coid=ConnectAttach(0,serv_pid,1,0,0);

printf("Connect res %d \n, vvedite soobshenie ", coid);

scanf("%s",&smsg);

printf("Vveli %s \n", smsg);

if(MsgSend(coid,smsg,strlen(smsg)+1,rmsg, sizeof(rmsg))==-1)

 {  

 printf("Error MsgSend \n");

}

return(1);

}

2.3.3 Последовательность действий

 

После компиляции программ сервера и клиента у нас будет 2 исполняемых файла. Назовём их server и client соответственно.

Программу server необходимо запустить в фоновом режиме # server &. Она начнёт работать: создаст канал, выведет номер канала и идентификатор процесса сервера, будет ждать сообщения от клиента.

Потом необходимо запустить клиента. Он попросит ввести идентификатор процесса сервера, для установления соединения с ним, и само сообщение (20 символов). Далее можно наблюдать, как сервер получит сообщение, выведет его и пошлёт ответ.

На этом клиент закончит свою работу, но его можно запустить ещё раз и послать другое сообщение.

Остановить работу сервера можно функцией kill < идентификатор процесса сервера >.

2.3.4 Результаты

 

# `pwd`/server &

[3] 2019364

# Prog server

Server start working

Chanel id: 1

Pid: 2019364

# `pwd`/client

Prog client, Vvedite PID servera

2019364

Vveli 2019364

Connect res 3

, vvedite soobshenie Hello_server

Vveli Hello_server

Polychili soobshenie, rcvid 2

Soobshenie takoe: "Hello_server".

"Eto otvet".

# `pwd`/client

Prog client, Vvedite PID servera

2019364

Vveli 2019364

Connect res 3

, vvedite soobshenie I_snova_Privet

Vveli I_snova_Privet

Polychili soobshenie, rcvid 2

Soobshenie takoe: "I_snova_Privet".

"Eto otvet".

# kill 2019364

#


Поделиться с друзьями:

Автоматическое растормаживание колес: Тормозные устройства колес предназначены для уменьше­ния длины пробега и улучшения маневрирования ВС при...

История развития пистолетов-пулеметов: Предпосылкой для возникновения пистолетов-пулеметов послужила давняя тенденция тяготения винтовок...

Поперечные профили набережных и береговой полосы: На городских территориях берегоукрепление проектируют с учетом технических и экономических требований, но особое значение придают эстетическим...

Кормораздатчик мобильный электрифицированный: схема и процесс работы устройства...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.047 с.