Материал: Технол_разраб_прогр_обесп_Гагарина_Кокарева

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

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

Благодаря СОМ, клиентам нет нужды учитывать данные отличия — доступ ко всему осуществляется единообразно. Для доступа к сервисам, предоставляемым любыми типами программного обеспечения, используется одна общая модель.

Microsoft применяет СОМ в большинстве продуктов; она используется для спецификации расширений Microsoft Windows и Microsoft Windows NT, а также для определения стандартных интерфейсов к различным типам сервисов. Выгоды применения СОМ в разработке всех типов программного обеспечения несомненны.

Разработку OLE-контейнеров и серверов проще всего вести в среде VisualC++ при помощи специальных мастеров (например, AppWizard), а также библиотеки MFC.

Порядок выполнения работы

1. Создать приложение Cnt, для этого:

  • чтобы приступить к созданию нового проекта, выберите в окне компилятора MicrosoftVisualC++ в меню File команду New;

  • в окне New выберите элемент MFC AppWizard(exe), в результате этого будет запущен мастер приложений, работа с которым осуществляется в шесть этапов:

  • в первом окне установите опцию Singledocument;

  • во втором окне не задавайте поддержку баз данных;

  • в третьем окне установите опцию Container, указывающую на то, что приложение будет OLE-контейнером. В этом же окне следует включить поддержку элементов управления ActiveX;

  • в следующем, четвертом окне необходимо оставить все опции, заданные по умолчанию;

  • в пятом окне установите опцию MFC Standard, опцию включения комментариев в программу и опцию статической компоновки библиотеки MFC;

  • наконец, в шестом окне просмотрите список классов, которые будут созданы автоматически, и щелкните по кнопке Finish;

  • мастер приложений отобразит окно с отчетом о сделанных установках. Если все правильно, щелкните по кнопке ОК, с тем чтобы запустить процесс генерации кода нового приложения;

  • осталось только построить исполняемый файл приложения, выбрав для этого в меню Build команду Rebuild All. В результате в папку DEBUG будет добавлен файл СМТ.ЕХЕ.

2. Проанализировать программный код.

Приложение включает пять основных исходных файлов, сгенерированных мастером AppWizard: CNT.CPP, MAINFRM.CPP, CNTDOC.CPP, CNTVIEW.CPP и CNTRITEM.CPP. Листинги файлов приведены в приложении 5.

  1. Внести, где это требуется, изменения.

  2. Отладить программы.

  3. Проверить работоспособность контейнера, для этого: • запустите приложение Сш (рис. Л6.1);

£е Безымянный - Cnt


ш

F& Мі Нф


Рис. Л6.1. Окно приложения Cnt


выберите в меню Edit команду InsertNewObject..., в результате чего откроется стандартное диалоговое окно вставки объекта;

в этом окне выделите элемент, соответствующий электронной таблице Excel (рис. Л6.2).


Вставка объекта


I Создание1 [ Создание из файла ] Тип объекта:


Видео апись

Диаграмма Microsoft Excel ШШ^Ш

Документ Image Документ Image Документ Microsoft Word Документ WordPad Звукозапись Клип мультимедиа

1

1 В виде значка


j-Резу


Вставка нового объекта типа "Диаграмма 1^ Microsoft Excel" в документ.


Рис. Л6.2. Выбор внедряемого объекта



6. Оценить результат (рис. Л6.3).


Безымянный - Cnt


Fife ШНф


F6




56 і

61

52^

321

35

37 і

: її'.

14

17^

і і!

~~~2


I t I j

-J L l I_J


Рис. Л6.3. Внедрение электронной таблицы Excel в приложение Cnt


  1. Создать приложение по выбору преподавателя.

  2. Сдать и защитить работу.


Защита отчета по лабораторной работе


Отчет по лабораторной работе должен состоять из:

  1. Листингов программ.

  2. Результатов работы.

Защита отчета по лабораторной работе заключается в предъявлении преподавателю полученных результатов (на экране монитора), демонстрации полученных навыков и ответах на вопросы преподавателя.


Контрольные вопросы

  1. Охарактеризуйте технологии OLE, СОМ и ActiveX.

  2. В чем заключаются преимущества и недостатки OLE?

  3. В чем заключаются преимущества и недостатки СОМ?

  4. Опишите развитие от OLE до ActiveX.

  5. Как связаны VisualC++ и OLE?



ЛАБОРАТОРНАЯ РАБОТА № 7. Создание сетевых приложений на Delphi с использованием Windows Sockets API





Цель работы: изучить метод разработки сетевых приложений в среде Delphi на низком уровне с использованием Winsock API. Лабораторная работа рассчитана на 4 академических часа.


Подготовка к лабораторной работе


1. Ознакомиться с лекционным материалом по теме «Сетевое
программное обеспечение» учебной дисциплины «Технология
разработки программного обеспечения».

  1. Изучить программирование в среде Delphi.

  2. Изучить программирование сетевых приложений.

  3. Изучить соответствующие разделы в изданиях [1, 2, 4, 5].


Теоретическая часть. Сетевые приложения


Для поддержки сетевых приложений существует технология, названная «сокеты». Сокет — это модель одного конца соединения со всеми присущими ему свойствами и методами. По сути, это прикладной программный интерфейс, входящий в состав многих операционных систем (ОС) и призванный для поддержки сетевых возможностей ОС. В стандарте структуры протоколов семиуровневой модели OSI-сокеты лежат на так называемом транспортном уровне, ниже находится сетевой протокол ip, а выше — протоколы сеансового уровня, такие как ftp, рорЗ, smtp и т. д.

В Windows поддержка сокетов включена, начиная с версии 3.11, и названа winsock. Для написания приложений с сетевой поддержкой существует специальный winsock api.

Все сетевые приложения построены на технологии «клиент — сервер»; это значит, что в сети существует, по крайней мере, одно приложение, являющееся сервером, типичная задача которого — это ожидание запроса на подключение от приложений-клиентов, которых может быть теоретически сколько угодно, и выполнение всевозможных процедур в ответ на запросы клиентов. Для клиент-серверной технологии абсолютно неважно, где расположены клиент и сервер — на одной машине или на разных. Конечно, для успешного соединения клиента с сервером клиенту необходимо иметь минимальный набор данных о расположении сервера — для сетей 1:ср/ір это ір-адрес компьютера, где расположен сервер, и адрес порта, на котором сервер ожидает запросы от клиентов.

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

Значение порта не обязательно должно совпадать на сервере и клиенте — клиенту для соединения важно только знать порт сервера, порт клиента может выбираться клиентом произвольно и становится известен серверу в момент запроса клиента на соединение. Когда соединение будет установлено, ОС создаст для серверного приложения соответствующий сокет, с которым и будет работать приложение, так что порт клиента для сервера совершенно не важен.

Механизм работы сокетов таков: на серверной стороне запускается серверный сокет, который после запуска сразу переходит в режим «прослушивания» (т. е. ожидания соединения клиентов). На стороне клиента создается сокет, для которого указывается ір-адрес и порт сервера и дается команда на соединение. Когда сервер получает запрос на соединение, ОС создает новый экземпляр сокета, с помощью которого сервер может обмениваться данными с клиентом. При этом сокет, который создан для «прослушивания», продолжает находиться в режиме приема соединений, таким образом, программист может создать сервер, работающий с несколькими подключениями от клиентов.

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

Рассмотрим минимальный набор функций из winsock арі, необходимых для написания элементарного клиента и сервера. Сами функции находятся в файле winsock.dll. Файл winsock.pas содержит необходимые объявления импортируемых функций winsock арі и базовые структуры данных. К сожалению, этот файл импортирует не все необходимые нам функции, и позже мы напишем свой файл импорта.

function wsastartup(wversionrequired: word; var wsdata: twsadata) integer; stdcall;

Функция сообщает ОС, что в любом процессе приложения могут быть использованы функции winsock. Функция должна быть вызвана один раз при запуске приложения перед использованием любой функции winsock.

function wsacleanup: integer; stdcall;

Функция сообщает ОС, что приложение более не использует winsock. Данная функция должна быть вызвана перед завершением приложения.

function socket(af, struct, protocol: integer) tsocket; stdcall;

Функция создает сокет. Порт и адрес задается в функции bind (сервер) или connect (клиент). Входящий параметр af — спецификация семейства сокетов (afjnet, af_ipx и др.), struct — спецификация типа нового сокета (принимает значение sock_stream или sock_dgram), protocol — специфический протокол, который будет использоваться сокетом. Если функция выполнена без ошибок, она возвращает дескриптор на новый сокет, если ошибки есть, возвращается invalid__socket.

function connect(s: tsocket; var name: tsockaddr; namelen: integer) integer; stdcall;

Функция соединения для клиента. Структура адреса содержит порт (необходимо привести функцией htons) и адрес (для

20*

клиента необходимо привести из имени или спецификации ip4 — ххх.ххх.ххх.ххх).

function bind(s: tsocket; var addr: tsockaddr; namelen: integer) integer; stdcall;

Функция ассоциирует адрес с сокетом. Структура адреса содержит порт (необходимо привести функцией htons) и адрес (для сервера обычно указывается inaddr_any — любой).

function send(s: tsocket; var buf; len, flags: integer) integer; stdcall;

Функция отправки данных. Помещает в очередь сокета s кусок данных из buf длиной len. Последний параметр отвечает за вид передачи сообщения. Может быть проигнорирован (0).

function recv(s: tsocket; var buf; len, flags: integer) integer; stdcall;

Функция получения данных.

Итак, рассмотрим примеры элементарного сервера и клиента. Договоримся, что сервер будет работать в асинхронном (блокирующем режиме). Единственная функциональность сервера — это получение строк данных от клиента и вывод их на экран. Связь клиента с сервером разрывается после получения строки, состоящей из единственного символа 'q'

Для обеспечения возможности подключения к серверу множества клиентов сервер на каждое соединение запускает отдельный поток.

program winsock_server;

//Простейшее приложение-сервер.

//Сокеты работают в блокирующем режиме.

//На каждое соединение создается отдельный поток.

{$apptype console}

uses

sysutils, winsock, windows; var

vwsadata twsadata; vlistensocket,vsocket tsocket; vsockaddr : tsockaddr;

trid thandle; const

cport word(33) ; csigexit 'q'

//Процедура отдельного потока для каждого клиента.

procedure socketthread;

var sockname tsockaddr;

abuf array of char;

vbuf string;

vsize integer;

s rtsocket;

bufsize integer;

begin

s vsocket;

if s invalid_socket then exit; vsize sizeof(tsockaddr); getpeername(s, sockname, vsize);

writeln(format(1 client accepted, remote address [%s] [inet_ntoa (sockname.sin_addr)])); //Определяем размер буфера чтения для сокета. vsize sizeof(bufsize);

getsockopt(s, sol_socket, so_rcvbuf,pchar(@ bufsize),vsize);

writeln(format(1 receive buffer size [%d] 1, [bufsize]));

setlength(abuf,bufsize);

repeat

//Получаем данные. Процедура работает в блокирующем //режиме, таким образом, следующая строка кода //не получит управление, пока не поступят данные //от клиента.

vsize recv(s, abuf[0],bufsize, 0);

if vsize<=0 then break;

setlength(vbuf,vsize) ;

lstrcpyn(@vbuf[1], @abuf[0],vsize);

writeln (format (1 received from cleint-: %s 1, [vbuf ]) ) ; until vbuf 'q'

writeln(format(1 client disconnected, remote address [%s] 1, [inet_ntoa(sockname.sin_addr)])); setlength(abuf, 0); closesocket(s); end; begin

writeln(1 starting application...

//Объявляем, что программа будет использовать //windows sockets.

if wsastartup($101,vwsadata)<>0 then halt(l); writeln('using windows sockets.'); //Создаем прослушивающий сокет. vlistensocket

socket(af_inet, sock_stream, ipproto_ip); writeln(format('creating socket on port [%d] [cport]));

if vlistensocket invalid_socket then halt(l); fillchar(vsockaddr, sizeof(tsockaddr),0); vsockaddr.sin_family af_inet; vsockaddr.sin_port htons(cport); vsockaddr.sin_addr.s_addr inaddr_any; writeln('binding socket. '); //Привязываем адрес и порт к сокету.

if bind(vlistensocket,vsockaddr,sizeof(tsockaddr)) 0

then halt(1);

//Начинаем прослушивать.

if listen(vlistensocket,somaxconn) <> 0

then halt(1);

writeln('socket status: listening, repeat

//Ожидаем подключения.

vsocket accept(vlistensocket, nil, nil); //Клиент подключился, запускаем новый процесс на соединение.

createthread(nil, 0, @socketthread, 0,0, trid); until false;

closesocket(vlistensocket) ;

wsacleanup;

end

В тексте программы использована не описанная ранее функция getpeernameO, которая возвращает информацию о канале, ассоциированном с сокетом. В данном контексте она нужна для получения информации о ip-адресе подключившегося клиента. Подробно об этой функции, как и о всех других функциях winsock, можно прочитать в windows sockets 2 application program interface, входящем в состав Win32 programmer's reference.

Приведенный выше код можно использовать только как учебное пособие. Для того чтобы использовать его в качестве основы для настоящего приложения, необходимо некоторое количество доработок, так как многие вещи, которые могут привести в будущем к серьезным ошибкам, были сознательно опущены для уменьшения размера кода и акцентирования внимания именно на аспектах использования winsock. Если вы недостаточно хорошо знакомы с понятием потоков (threads) в windows, то вам лучше использовать класс tthread, существующий в delphi специально для поддержки многопоточных приложений. Исходный код клиента представлен ниже:

program winsock_client; {$apptype console} uses

sysutils,

winsock;

const

cport 33; csigexit 'q' var

vwsadata twsadata; vsocket tsocket; vsockaddr tsockaddr; buf string; begin

if wsastartup($101,vwsadata)<>0 then halt(l); vsocket socket(af_inet, sock_stream, ipproto_ip); if vsocket invalid_socket then halt(l); fillchar(vsockaddr, sizeof(tsockaddr), 0); vsockaddr.sin_family af_inet; vsockaddr.sin_port htons(cport);

vsockaddr.sin_addr.s_addr inet_addr('127.0.0.1'); if connect(vsocket, vsockaddr, sizeof(tsockaddr)) socket_error then halt(l); repeat readln(buf);

if send(vsocket,buf[1],length(buf),0) socket_error

then break;

until buf csigexit;

closesocket(vsocket) ;

wsacleanup;

end

Итак, мы рассмотрели, как работают сокеты в асинхронном режиме. Давайте посмотрим теперь, какие возможности winsock нам предоставляет для работы с неблокирующими сокетами.

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

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

function select(nfds: integer; readfds, writefds, exceptfds: pfdset; timeout: ptimeval) longint; stdcall;

Эта функция позволяет контролировать состояние набора сокетов.

Аргумент nfds игнорируется и оставлен только для совместимости. Должен быть равен 0. readfs, writefds, exceptfds — указатели на наборы сокетов, для которых нужно контролировать состояние чтения, отправки данных и ошибок соответственно. Наборы хранятся в структуре pfdset, управление которой осуществляется специальными макросами, описанными в winsock.pas:

procedure fs_zero(var fdset: tfdset);

Обнуляет структуру, устанавливает количество контролируемых сокетов в 0.

procedure fd_set(socket: tsocket; var fdset: tfdset);

Добавляет указанный сокет в структуру.

procedure fd_clr(socket: tsocket; var fdset: tfdset);

Удаляет указанный сокет из структуры.

function fd_isset(socket: tsocket; var fdset: tfdset) boolean

Возвращает true, если указанный сокет является членом указанной структуры.

Аргумент timeout является ссылкой на структуру типа ptimeval, в которой можно указать время ожидания срабатывания функции select. В случае указания в качестве значения времени задержки 0 или nil в качестве аргумента timeout функция select будет ждать бесконечно, как при выполнении операции в блокирующем режиме.

Как видно из описания, функция может следить сразу за несколькими сокетами, таким образом, теперь мы можем либо также запускать отдельный процесс на каждый открытый сокет, который будет следить за конкретным сокетом, либо создать один процесс, который будет следить за всеми открытыми сокетами. В первом случае нам потребуются лишь незначительные изменения процедуры socketthread, во втором случае потребуются довольно значительные изменения.

Давайте рассмотрим логику работы во втором случае, так как первый случай банален.

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

arg 1;

ioctlsocket(socket, fionbio,arg);

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

arg 0 ;

ioctlsocket(socket, fionbio, arg);

Затем необходимо реализовать возможность сохранения каждого сокета в некоторый массив sockarray. Далее, нужно обеспечить, чтобы поток, который будет заниматься обработкой клиентов, запускался только один раз. Это сделать несложно, зная, что процедура createthread возвращает в переменную ссылку на вновь созданный поток, которую и нужно использовать для проверки, был ли создан поток. В потоке обработки нужно внести следующие изменения для использования функции select:

Введем новые переменные wfds :tfdset; integer; tv ttimeval.

Затем определим основной цикл, в котором будем обрабатывать данные:

repeat


until connum>0;

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

fd_zero(wfds);

for :=1 to connum do

begin

fd_set(sock[i], wfds) ; end;

Далее, указываем в структуре tv время задержки для функции select:

tv.tv_sec 5; tv.tv_usec 0;

Теперь можно вызывать функцию select (так как мы следим только за приемом данных, то в качестве writefds, exceptfds мы указываем nil):

select(0, @wfds, nil, nil, @tv);

Теперь, когда функция select возвратит управление переменной wfds, мы будем иметь набор сокетов, для которых необходимо произвести чтение, и можем обработать поступившие данные:

if wfds.fd_count=0 then continue; for :=0 to wfds.fd_count-l do begin

vsocket wfds.fd_array[i];

//Обработка поступивших данных с сокета vsocket. end;

Условие выхода из основного цикла — отсутствие открытых сокетов в массиве sockarray. Условия закрытия сокета мы оставляем на совести читателя, добавим только лишь, что сокет попадет в обработку select и при наступлении события разрыва связи (для обработки можно использовать то, что количество принятых байтов функцией recv будет равно нулю, а также функцию wsagetlasterror).

Давайте теперь разберемся, для чего мы указали время ожидания функции send, а не сделали его бесконечным? Дело в том, что в текущей реализации при создании нового сокета он не попадет в обработку select, пока не будет установлен функцией fs_set, что, естественно, не пройдет в блокирующем режиме, пока select не возвратит управление по событию с одним из отслеживаемых клиентов. Установка значения timeout гарантирует, что сокет попадет в обработку независимо от состояния других сокетов.

Какой вариант использовать — поток на каждый сокет, либо один поток обрабатывает все соединения — вопрос, к которому нужно подходить по-разному в каждом конкретном случае. Если в процессе общения с клиентом серверу требуется значительное время на подготовку и обработку данных, то, конечно, следует создать разные потоки, чтобы другим клиентам не пришлось ожидать завершения обработки, а работать параллельно. В противном случае, если обработка минимальна, то создание большого количества процессов окажет влияние в худшую сторону на общую производительность системы.

Помимо функции select существует еще два метода работы с асинхронными сокетами:

function wsaasyncselect(s: tsocket; hwindow: hwnd; wmsg: u_int; levent: longint) integer; stdcall;

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

const

wm_mysocket wm_user + 1; type

tforml class(tform) private

procedure socket_proc(var msg:tmessage);message wm_mysocket;

wsaasyncselect(vsocket,forml.handle, wm_ mysocket, fd_accept+fd_read);

procedure tforml.socket_proc(var msg: tmessage); begin

if ((msg.msg wm_mysocket) and (msg.lparam fd_accept)) then showmessage(*connected'); end;

Рассмотрим еще один способ, который пригодится, если у вас нет окна приложения; этот способ основан на системных событиях (events). К сожалению, файл winsock.pas не импортирует соответствующие функции, в результате этого многие программисты пренебрегают возможностями событий. Напишем собственный импорт необходимых процедур:

function wsaeventselect(s: tsocket; event: thandle;

levent: longint):integer;stdcall;

external 'ws2_32.dll* name 'wsaeventselect*

function wsawaitformultipleevents(ncount: dword;

lphandles: pwohandlearray;

bwaitall: bool; dwmilliseconds: dword;

falertable:bool):integer;stdcall;

external *ws2_32.dll* name *wsawaitformultipleevents* function wsacreateevent:thandle;stdcall; external *ws2_32.dll* name 'wsacreateevent' function wsaresetevent(event thandle):bool;stdcall; external 'ws2_32.dll* name 'wsaresetevent' function wsaenumnetworkevents(const s tsocket; const event thandle; lpnetworkevents lpwsanetworkevents) longint stdcall;far;

external 'ws2_32.dll* name 'wsaenumnetworkevents* function wsacloseevent(event thandle):integer; stdcall; external 'ws2_32.dll' name 'wsacloseevent*

Также нам потребуется описание структуры wsanetworkevents.

const

fd_max_events 10; type

twsanetworkevents record lnetworkevents: longint;

ierrorcode: array[0..fd_max_events-l] of integer; end;

pwsanetworkevents Pwsanetworkevents; lpwsanetworkevents pwsanetworkevents;

Принцип работы с этим набором функций состоит в создании специального объекта типа event, затем происходит связывание этого события с сокетом с помощью функции wsaeventselect, в которой также указывается набор отслеживаемых состояний сокета. Один сокет может быть связан только с одним объектом типа event.

Затем, в цикле обработки мы организуем ожидание поступления события от сокета; это реализуется с помощью api функций waitforsingleobject — для ожидания одного события либо waitformultipleobjects — для ожидания набора событий. При наступлении события функция возвращает управление. Для однозначной идентификации, от какого сокета пришло уведомление, используется функция wsaenumnetworkevents, возвращающая структуру типа twsanetworkevents.

var

fevent thandle; //Создаем серверный сокет

feventclose wsacreateevent;

wsaeventselect(socket,fevent, fd_close fd_read repeat

waitforsingleobject(fevent, infinite);

wsaenumnetworkevents(fsocket, fevent, @ni);

case ni.lnetworkevents of

fd_close:break;

fd_read: begin

receivedata;

end;

end;

wsaresetevent(feventclose) ; until false;

wsacloseevent(feventclose);

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

Рассмотрим отправку данных. Дело в том, что, как мы уже говорили, отправка данных есть, по сути, постановка порции данных в очередь. Мы не можем управлять отдельными пакетами, более того, данные, попавшие в буфер, отправляются не сразу, а могут накапливаться для отправки в дальнейшем одним пакетом. Таким образом, последовательный вызов

send(vsocket, @buf1,length(buf1),0); send(vsocket,@buf2,length(buf2) , 0);


фактически будет идентичен одному вызову send с объединенным буфером bufl+buf2.

Таким образом, при приеме данных, хотя мы послали две порции данных, мы получим одну. Совсем другой случай, когда мы посылаем порцию данных, большую, чем буфер сокета, тогда функция send отправит только часть данных из указанного буфера, ровно столько, сколько влезло в буфер сокета. Для того чтобы отследить эту ситуацию и отправить необработанную часть буфера, нужно воспользоваться тем, что функция send возвращает количество фактически отосланных данных. Функцию send нужно запускать в цикле, условием завершения которого будет полная отправка всего буфера.

При чтении из сокета данных мы можем наблюдать как бы «склейку» порций данных либо, наоборот, фрагментацию (не путать с фрагментацией пакетов на уровне tcp/ip). Такие ситуации должна обрабатывать наша программа. Решить проблему можно добавлением сигнатуры признака конца блока данных. Это имеет смысл, если приложения часто обмениваются небольшими блоками данных, где чаще всего возникает эффект «склеивания», но неэффективны при больших объемах, так как сканирование большого буфера на предмет сигнатуры отнимает много времени. Обычно это решается таким способом — в начале каждого пакета добавляется 32-битовое число, определяющее длину порции данных в байтах. Таким образом, принимающая часть, зная размер каждого блока, может распознать «склейку» и фрагментацию.

Об остальных аспектах сетевого программирования с использованием библиотеки winsock вы можете узнать из справки «windows sdk» в разделе «windows sockets 2 application program interface*.

Порядок выполнения работы

  1. Написать сетевое приложение с использованием \Vinsock АР1 в соответствии с заданным преподавателем вариантом. При этом один компьютер — сервер, другой — клиент.

  2. Отладить программу.

  3. Произвести обмен данными с соседним компьютером

  4. Изменить направление «клиент — сервер».

  5. Еще раз обменяться данными.

  6. Закончить работу с сокетами.

  7. Сдать и защитить работу.



Защита отчета по лабораторной работе


Отчет по лабораторной работе должен состоять из:

  1. Листингов программ.

  2. Результатов работы.

Защита отчета по лабораторной работе заключается в предъявлении преподавателю полученных результатов (на экране монитора), демонстрации полученных навыков и ответах на вопросы преподавателя.


Контрольные вопросы

  1. Какое существует сетевое программное обеспечение?

  2. Охарактеризуйте архитектуру «клиент — сервер».

  3. Приведите понятие сокета.

  4. Приведите понятие порта.

  5. Какие существуют функции для работы с сокетами?

  6. Охарактеризуйте синхронные и асинхронные операции ввода-вывода.



Варианты заданий


1. На базе примера написать чат. Программа должна передавать самой себе по \VinSocket сообщения в обычном и кодированном виде. Использовать код Цезаря. Суть кода: все буквы сдвинуты на три позиции, то есть: «а» шифруется буквой «г»,

«б»—«д» и так далее, «э»—«а», «ю»—«б», «я»—«в». Аналогично сдвигается английский алфавит.

  1. На базе примера написать чат. Программа должна передавать самой себе по WinSocket сообщения с присоединенными к ним файлами (бинарными в общем случае).

  2. Написать интернет-игру. Программа должна передавать самой себе по WinSocket координаты точки. Эта точка должна или рисоваться, или должны выводиться ее координаты, либо указывать на ячейку таблицы Excel, либо отображаться каким-либо иным способом.

  3. Написать программу, которая следит за использованием соединения и подсчитывает статистику передачи сообщений между двумя пользователями. Программа должна уметь работать на отдельном компьютере. В качестве программ пользователей использовать пример или программы из 1 —3 вариантов.

  4. Написать распределенную базу данных. Одна программа посылает запросы на получение данных и на сохранение изменений в этих данных. Другая программа работает с таблицей Excel, читает из нее запрашиваемые данные или записывает данные в таблицу.

Таблицу можно не отображать на экране.

6. Написать «защищенную сеть». По нажатию кнопки в дис-
петчерской программе все указанные соединения должны за-
крываться. В диспетчерской программе должен быть список от-
крытых соединений. В качестве пользовательских программ ис-
пользовать пример или программы из 1—3 вариантов.

ЛАБОРАТОРНАЯ РАБОТА № 8.

Проектирование программной системы

при объектном подходе к программированию

Цель работы: познакомить студентов с методом проектирования системы путем СЯС-карт.

Лабораторная работа рассчитана на 4 академических часа.

Подготовка к лабораторной работе

  1. Ознакомиться с лекционным материалом по теме «Проектирование программных продуктов при объектном подходе» учебной дисциплины «Технология разработки программного обеспечения».

  2. Изучить соответствующие разделы в изданиях [1—3, 48, 49, 51].

3. Ознакомиться с разд. 3.6, 4.5 данного пособия.

Теоретическая часть. Основы иЛМ-проектирования

Важным этапом создания программного обеспечения является проектирование. На этом шаге закладывается архитектура системы.

Одним из способов проектирования является метод СКС-карточек. Этот метод проектирования является составляющей иМЬ-проектирования.

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

Пример «Банкомат».

Диаграмма вариантов использования для примера «Банкомат» приведена на рис. Л8.1.

Система банкомата

Рис. Л8.1. Диаграмма вариантов использования «Банкомат»

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

Эта диаграмма дает понять, что будет делать система, как она будет функционировать. Диаграмма использования бывает также очень полезна для общения с заказчиком — она позволяет показать наиболее значимые действия системы и проверить, правильно ли вы поняли заказчика и значимость отдельных функций для него.

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

Придумать можно много {таймер, счетчик купюр, карточка и т. д.).

Далее оформляются СЛС-карты. Это листки бумаги 10 х 15. Они разделены на три части и выглядят следующим образом — рис. Л8.2.

На примере того же банкомата — рис. Л8.3.

Клиент

1. Вставляет карточку в банкомат.

Банкомат

2. Вводит пароль.


3. Указывает тип операции (снять деньги,


просмотреть остаток).


4. Вводит сумму.


5. Получает деньги.


6. Вынимает карточку


а

Банкомат 1

1. Отображает информацию для клиента.

Клиент

2. Передает информацию в банк.

Банк

3. Отсчитывает купюры.

Служба безопасности банка

4. Распечатывает счет


б

Служба безопасности банка

1. Проверяет пароль.

Банк

2. Проверяет подлинность карточки.

Банкомат

3. Идентифицирует клиента.


4. Следит за правильностью транзакций


операций с деньгами


в

Банк

1. Проверяет возможность выдачи средств.

Банкомат

2. Сообщает о наличии денег.

Служба безопасности банка

3. Выдает информацию об остатке.


4. Хранит информацию о счете клиента


Рис. Л8.3. Примеры СЯС-карт

Шаг третий. Для проверки достаточности или избыточности придуманных классов, а также корректности их взаимодействия строится диаграмма взаимодействия (рис. Л8.4).

Клиент Банкомат

Вводит пароль

Служба безопасности


Передает пароль

Банк

Выдает сообщение с результатом

Вводит сумму г*-

Производит транзакцию с суммой



Передает информацию о возможности выдать сумму

Выдает сумму

I


Рис. Л8.4. Диаграмма взаимодействия

Метод СКС-карточек позволяет провести также инсценировку работы системы. Для этого достаточно раздать карточки с классами участникам проекта. После этого начать ролевую игру. Первый участник встает и читает действие, совершаемое его классом. Другие участники, исходя из своих карточек, сообщают об ответной реакции других классов. Если в какой-то момент реакции не последует, то это признак несовершенства проекта системы. Такая игра может подсказать и об избыточности проекта.


Порядок выполнения работы

  1. В соответствии с вариантом задания, предложенным преподавателем, определить действующих лиц (акторов) системы.

  2. Определить варианты использования системы и описать их в краткой или полной форме (см. разд. 3.6.2).

  3. Построить диаграмму вариантов использования системы (использовать MS Office или MS Visio).

4. Определить классы проектируемой системы.

5. Создать CRC-карты для всех классов системы (использо-
вать
MS Office или MS Visio).

6. Построить диаграмму взаимодействия (использовать MS
Office или MS Visio).

7. Сдать и защитить работу.


Защита отчета по лабораторной работе


Отчет по лабораторной работе должен состоять из:

  1. Постановки задачи.

  2. Описания действующих лиц и прецедентов системы.

  3. Диаграммы прецедентов.

  4. СЯС-карты.

  5. Диаграммы взаимодействия.

Защита отчета по лабораторной работе заключается в предъявлении преподавателю полученных результатов (на экране монитора), демонстрации полученных навыков и ответах на вопросы преподавателя.


Контрольные вопросы

  1. Охарактеризуйте проектирование ПО при объектном подходе.

  2. В чем заключается моделирование предметной области при проектировании ПО?

  3. Язык иМ1_. Его назначение, преимущества и недостатки.

  4. Опишите варианты использования ПО.

  5. Перечислите диаграммы в языке иМ1_.

  6. Приведите пример диаграммы прецедентов.

7 Приведите пример диаграммы взаимодействия.

8. В чем состоит назначение и использование СРС-карт?



Варианты заданий

  1. Заказ билетов в аэропорту.

  2. Электронный магазин.

  3. Отправка біш.

  4. Система охраны частного дома.

  5. Система безопасности тюрьмы.

  6. Система безопасности полета самолета.

ЛАБОРАТОРНАЯ РАБОТА № 9. Динамические структуры данных






Цель работы: научиться использовать динамические структуры данных при создании программного обеспечения.

Лабораторная работа рассчитана на 4 академических часа.



Подготовка к лабораторной работе


1. Ознакомиться с лекционным материалом по теме «Струк-
тура и формат данных» учебной дисциплины «Технология разра-
ботки программного обеспечения».

  1. Изучить соответствующие разделы в изданиях [40, 41, 47].

  2. Повторить программирование на С++ (Delphi).

  3. Ознакомиться с разд. 3.3 данного пособия.


Теоретическая часть. Статические и динамические данные


При написании программ необходимо постоянно использовать переменные для хранения данных. При обычном объявлении переменной целочисленного массива на языке С++ (здесь и далее в качестве языка выбран С++, но аналогичные структуры данных есть и в других языках высокого уровня):

int mas[100];

на этапе компиляции программы происходит автоматическое резервирование памяти под эту переменную (рис Л9.1, а). Это имеет ряд серьезных недостатков:

• невозможность вернуть компьютеру неиспользуемую память. Например, большой массив будет использоваться только один раз, а память нельзя освободить до окончания работы программы;

• захват памяти, которая вообще может не использоваться программой. Эта ситуация, к сожалению, также не редкость. Зачастую трудно заранее определить размер массива, который понадобится для работы, и под него отводят память «с запасом». Для решения описанных проблем используются динамические структуры данных. С помощью этого типа данных необходимую память можно выделять в ходе программы и возвращать компьютеру, когда она становится ненужной для работы. Например:

float *a; //Объявляем указатель на переменную а

//(переменная, в которой хранится
//адрес
ячейки данных)
a=new float; //Выделяем память под переменную
&а=7.89; //Присваиваем значение переменной

delete (а); //Удаляем переменную и возвращаем память





А23В:0004








Рис. Л9.1. Распределение памяти при объявлении: а — статических переменных; б — динамических переменных

Указатель — адрес ячейки памяти. При использовании оператора new компьютер выделяет необходимую память, и адрес этой ячейки памяти записывается в переменную-«указатель» (в примере — это переменная а).


Создание списка

Список — последовательность ячеек памяти, хранящих элементы массива данных. Каждая ячейка такого списка состоит как минимум из двух частей: в первой хранится элемент массива, во второй — указатель на следующую такую же ячейку памяти (рис. Л9.2).

Ниже приводится код программы для работы со списком на языке С++. Используя его, познакомимся с технологией создания списка.

Для работы со списками, как говорилось выше, используется структура, состоящая как минимум из двух элементов:

struct spisok {

int elem; //Данные

spisok *next; //Указатель, адрес следующего элемента };

В отличие от простого объявления переменной, для динамических данных добавляется значение «звездочка» (оно означает, что это адрес памяти, где расположен элемент, но не он сам). Чтобы в ходе программы получить значение элемента по известному адресу или, наоборот, присвоить значение, используется знак & (&а-56).

1. Далее в программе следует строка:

spisok *1;

Это адрес первого элемента списка. Его необходимо знать всегда.

Источник: https://files.student-it.ru/previewfile/6671