Смекни!
smekni.com

Учебно-методическое пособие рекомендовано учебно-методическим советом Международного университета природы, общества и человека (стр. 6 из 11)

В случае удачного завершения функция CreateMutex возвращает дескриптор созданного мьютекса. В случае неудачи эта функция возвращает значение NULL. Если мьютекс с заданным именем уже существует, то функция CreateMutex возвращает дескриптор этого мьютекса, а функция GetLastError, вызванная после функции CreateMutex вернет значение ERROR_ALREADY_EXISTS.

Мьютекс захватывается потоком посредством любой функции ожидания, а освобождается функцией ReleaseMutex, которая имеет следующий прототип:

BOOL ReleaseMutex(

HANDLE hMutex // дескриптор мьютекса

);

В случае успешного завершения функция ReleaseMutex возвращает значение TRUE, в случае неудачи – FALSE. Если поток освобождает мьютекс, которым он не владеет, то функция ReleaseMutex возвращает значение FALSE.

Для доступа к существующему мьютексу поток может использовать одну из функций CreateMutex или OpenMutex. Функция CreateMutex используется в тех случаях, когда поток не знает, создан или нет мьютекс с указанным именем другим потоком. В этом случае значение параметра bInitialOwner нужно установить в FALSE, так как невозможно определить какой из потоков создает мьютекс. Если поток использует для доступа к уже созданному мьютексу функцию CreateMutex, то он получает полный доступ к этому мьютексу. Для того чтобы получить доступ к уже созданному мьютексу, поток может также использовать функцию OpenMutex, которая имеет следующий прототип:

HANDLE OpenMutex(

DWORD dwDesiredAccess, // доступ к мьютексу

BOOL bInheritHandle // свойство наследования

LPCTSTR lpName // имя мьютекса

);

Параметр dwDesiredAccess этой функции может принимать одно из двух значений:

MUTEX_ALL_ACCESS

SYNCHRONIZE

В первом случае поток получает полный доступ к мьютексу. Во втором случае поток может использовать мьютекс только в функциях ожидания, чтобы захватить мьютекс, или в функции ReleaseMutex, для его освобождения. Параметр bInheritHandle определяет свойство наследования мьютекса. Если значение этого параметра равно TRUE, то дескриптор открываемого мьютекса является наследуемым. В противном случае – дескриптор не наследуется.

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

Практическая часть

Пример 1

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

Разрешение проблемы незнания программы о существовании своей копии.

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

Для начала объявим глобальные переменные:

volatile BOOL Exit;

HANDLE hMutex;

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

Наша функция (фрагмент кода можно интегрировать в функцию нажатия на кнопку в форме):

if (OpenMutex(SYNCHRONIZE, FALSE, "myMutex") == NULL)

hMutex = CreateMutex(NULL, FALSE, "myMutex");

else

hMutex = OpenMutex(SYNCHRONIZE, FALSE, "myMutex");

HANDLE hFirst = CreateThread(NULL, 0, FirstFunc, NULL, NULL, NULL);

HANDLE hSecond = CreateThread(NULL, 0, SecondFunc, NULL, NULL, NULL);

HANDLE hThird = CreateThread(NULL, 0, ThirdFunc, NULL, NULL, NULL);

Функция потока:

DWORD WINAPI FirstFunc (LPVOID pParam)

{

while(!Exit)

{

if (WaitForSingleObject(hMutex, INFINITE) == WAIT_OBJECT_0)

{

for (int i = 0; i<= 4; i++)

Beep(100, 250);

ReleaseMutex(hMutex);

}

}

return (0);

}

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

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


ПРАКТИЧЕСКОЕ ЗАНЯТИЕ №4. СЕТЕВОЕ ВЗАИМОДЕЙСТВИЕ В WINDOWS

Цель работы

Изучить механизм сокетов. Научиться разграничивать функциональность ПО между клиентской и серверной частью.

Порядок выполнения практических заданий

1. Рассмотреть представленные примеры, и разработать приложения на их основе.

2. Разработать алгоритм решения третьего задания, с учетом разделения функциональности между клиентом и сервером. Определить формат обмена информацией между клиентом и сервером.

3. Реализовать алгоритм с применением функций WinAPI и протестировать его на нескольких примерах.

Литературные источники

1. Э.Таненбаум. Распределённые системы. Принципы и парадигмы / Э.Таненбаум, Танненбаум, М. ванн Стесн. — СПб.:Питер, 2003. — 877с.

2. Эндрюс Г.Р. Основы многопоточного, параллельного и распределённого программирования/ Эндрюс Г.Р. — М.: «Вильямс», 2003. — 512с.

3. Уолтон Ш. “Создание сетевых приложений в среде Linux” /Уолтон Ш. 2001

Теоретическая часть

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

Отметим, что реализация сокетов в Unix и Windows значительно отличается, что создаёт очевидные проблемы.

Основное подспорье в изучении сокетов - Windows Sockets 2 SDK. SDK - это документация, набор заголовочных файлов и инструментарий разработчика. Большинство книг, имеющиеся на рынке, явно уступают Microsoft в полноте и продуманности описания. Единственный недостаток SDK - он полностью на английском (для некоторых студентов это очень существенно).

Обзор сокетов

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

Сокеты позволяют работать со множеством протоколов и являются удобным средством межпроцессорного взаимодействия, но в данной практической работе речь будет идти только о сокетах семейства протоколов TCP/IP, использующихся для обмена данными между узлами сети Интернет. Все остальные протоколы, такие как IPX/SPX, NetBIOS могут быть изучены студентами самостоятельно.

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

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

Замечание: дейтаграммные сокеты опираются на протокол UDP, а потоковые - на TCP.

Первый шаг

Для работы с библиотекой Winsock 2.х в исходный тест программы необходимо включить директиву "#include <winsock2.h>", а в командной строке линкера указать "ws2_32.lib". В Microsoft Visual Studio для этого достаточно нажать <Alt-F7>, перейти к закладке "Link" и к списку библиотек, перечисленных в строке "Object/Library modules", добавить "ws2_32.lib", отделив ее от остальных символом пробела.

Перед началом использования функций библиотеки Winsock, ее необходимо подготовить к работе вызовом функции "int WSAStartup (WORD wVersionRequested, LPWSADATA lpWSAData)", передав в старшем байта слова wVersionRequested номер требуемой версии, а в младшем - номер подверсии.

Аргумент lpWSAData должен указывать на структуру WSADATA, в которую при успешной инициализации будет занесена информация о производителе библиотеки. Никакого особенного интереса она не представляет и прикладное приложение может ее игнорировать. Если инициализация проваливается, функция возвращает ненулевое значение.

Программирование сокета начинается с создания объекта «сокет». Это осуществляется функцией "SOCKET socket (int af, int type, int protocol)" Первый слева аргумент указывает на семейство используемых протоколов. Для Интернет-приложений он должен иметь значение AF_INET.

Следующий аргумент задает тип создаваемого сокета - потоковый (SOCK_STREAM) или дейтаграммный (SOCK_DGRAM) (еще существуют и сырые сокеты, но они не поддерживаются Windows).

Последний аргумент уточняет какой транспортный протокол следует использовать. Нулевое значение соответствует выбору по умолчанию: TCP - для потоковых сокетов и UDP для дейтаграммных. В большинстве случаев нет никакого смысла задавать протокол вручную и обычно полагаются на автоматический выбор по умолчанию.

Если функция завершилась успешно, она возвращает дескриптор сокета, в противном случае - INVALID_SOCKET.

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