Главная страница

Программирование в сетях Windows. Э. Джонс, Д. Оланд


Скачать 2.88 Mb.
НазваниеЭ. Джонс, Д. Оланд
АнкорПрограммирование в сетях Windows.pdf
Дата12.10.2017
Размер2.88 Mb.
Формат файлаpdf
Имя файлаПрограммирование в сетях Windows.pdf
ТипКнига
#9346
страница8 из 50
1   ...   4   5   6   7   8   9   10   11   ...   50
Безопасность
Прежде чем приступить к теме безопасности и правил доступа к ресурсам в сети, обсудим основы обеспечения безопасности на локальной машине.
Windows NT и Windows 2000 позволяют управлять доступом к отдельным файлам и папкам как локально, так и по сети. При попытке приложения по- лучить доступ к таким ресурсам ОС проверяет, имеет ли это приложение со- ответствующие права. Основные виды доступа — чтение, запись и выполне- ние. Windows NT и Windows 2000 управляют доступом на основе дескрип-
торов безопасности (security descriptors) и маркеров доступа (access tokens).
Дескрипторы безопасности
Все защищенные объекты обладают дескриптором безопасности, содержа- щим информацию о порядке доступа к объекту. Дескриптор безопасности состоит из структуры SECURITY JDESCRIPTOR и связанной с ним информации о безопасности, включающей:
Ш идентификатор безопасности (Security Identifier, SID) владельца
определяет владельца объекта;
SID группы — определяет основную группу, в которую входит владелец объекта;
Ш избирательный список управления доступом (Discretionary Access
Control List, DACL) — указывает, кто и какой тип доступа (чтение, запись,
выполнение) имеет для данного объекта;
Ш системны й список управления доступом (syste m access control
list, SACL) — задает типы доступа к данному объекту, для которых гене- рируются записи в журнал аудита.
Приложения не могут напрямую изменять содержимое структуры деск- риптора безопасности. Впрочем, для этого можно использовать API-интер- фейсы безопасности Win32, содержащие соответствующие функции (мы продемонстрируем, как это сделать в конце главы).
Списки и записи управления доступом
Поля DACL и SACL в дескрипторе безопасности — это списки управления
доступом (access control lists, ACL), содержащие ноль или более записей уп-
равления доступом (access control entities, АСЕ). Каждая АСЕ управляет дос- тупом или осуществляет контроль за доступом к объекту определенного пользователя или группы и содержит:
И SID пользователя или группы, к которым применяется АСЕ;
И маску, задающую права доступа (чтение, запись, выполнение);
* флаг, обозначающий тип АСЕ — разрешение на доступ, запрет на доступ или системный аудит.
Заметим, что системный аудит применяется только в списках SACL, а типы разрешение и запрет на доступ — в списках DACL (рис. 2-2).

ЧАСТЬ I Устаревшие сетевые API
SID владельца
SID группы
DACL
SACL
Разрешить запись для Jim
АСЕ
Запретить чтение для
Anthony
Разрешить чтение для Net Team
АСЕ
АСЕ
Дескриптор безопасности
Рис. 2-2. Объект файла с соответствующим ему DACL
Если защищенный объект не имеет списка DACL (его DACL был обнулен
API-функцией SetSecurityDescriptorDad), то система предоставляет всем пол- ный доступ. Если объект имеет непустой DACL, система предоставляет толь- ко те типы доступа, которые явно указаны в записях АСЕ данного DACL. Если в списке нет ни одной записи АСЕ, система не предоставляет никому ника- кого доступа. Если же DACL содержит несколько разрешающих доступ запи- сей АСЕ, система неявно запрещает доступ всем пользователям и группам, не включенным в список.
В большинстве случаев задают только разрешающие доступ записи АСЕ,
за одним исключением: если имеется разрешающая запись для группы, но нужно запретить доступ каким-либо членам этой группы с помощью АСЕ.
При этом запрещающая доступ запись АСЕ должна обязательно предшество- вать записи, открывающей доступ всей группе, поскольку система читает за- писи по порядку и прекращает чтение, выяснив, что доступ данному пользо- вателю открыт или закрыт. То есть если запись, разрешающая доступ груп- пе, будет идти первой, система прочтет ее и предоставит доступ неполно- мочному пользователю из состава группы.
На рис. 2-2 показан список DACL, предоставляющий доступ для чтения группе Net Team. Эта группа состоит из пользователей Anthony, Jim и Gary, и нужно предоставить право чтения всем, кроме Anthony. Поэтому запись, зап- рещающая доступ пользователю Anthony, предшествует записи, разрешаю- щей доступ группе Net Team На рис. 2-2 также показана запись АСЕ, предос- тавляющая пользователю Jim доступ для записи. Напомним, что приложения не могут напрямую работать с АСЕ, а используют для этого специальные API- интерфейсы.
Идентификаторы безопасности
Дескрипторы безопасности и записи АСЕ защищенных объектов содержат
SID — уникальный код для идентификации учетной записи пользователя,

Г Л А В А 2 Перенаправитель Ы
группы или сеанса. Центр безопасности (например, домен Windows NT)
хранит информацию о SID в специальной базе данных Когда пользователь входит в систему, его SID извлекается из БД, помещается в маркер доступа пользователя и применяется для идентификации пользователя при всех про- верках безопасности.
Маркеры доступа
При входе пользователя в систему Windows NT проверяются его реквизиты:
имя учетной записи и пароль. Если вход разрешен, система создает маркер доступа и заносит в него SID пользователя. Каждый процесс, запущенный от имени этого пользователя, получит копию маркера. При попытке доступа процесса к защищенному объекту SID в маркере доступа сравнивается с SID
в списках DACL для определения прав доступа.
Сетевая безопасность
Рассмотрим теперь обеспечение безопасности при доступе к объектам по сети. Напомним, что за доступ к ресурсам на удаленных компьютерах отве- чает перенаправитель MSNP. Для этого он устанавливает безопасное соеди- нение клиент-сервер, создавая реквизиты сеанса пользователя.
Реквизиты сеанса
Существуют реквизиты пользователя двух типов: основные реквизиты входа
(primary login) и реквизиты сеанса. Когда пользователь регистрируется на рабочей станции, вводимые им имя и пароль становятся его основными рек- визитами входа и заносятся в маркер доступа. В один момент времени пользо- ватель может иметь единственный набор реквизитов. При попытке доступа к удаленному ресурсу (как через сетевой диск, так и по UNC-имени) пользова- тельские реквизиты входа используются для проверки прав доступа к этому объекту.
В Windows NT и Windows 2000 можно указать другой набор реквизитов для удаленного доступа по сети. Если реквизиты пользователя действитель- ны, перенаправитель MSNP создает сеанс связи между компьютером пользо- вателя и удаленным ресурсом. При этом он сопоставляет сеансу реквизиты,
состоящие из копии реквизитов, примененных компьютером пользователя для доступа к ресурсу. При каждом сеансе связи между компьютером и уда- ленным сервером используется только один набор реквизитов. Например,
если на машине В имеются общие папки \Hack и \Slash, и пользователь с машины А подключает папку \Hack как диск G:, а папку \Slash — как диск Н:,
то оба сеанса связи будут применять одинаковые реквизиты сеанса, так как устанавливают соединение с одним и тем же удаленным сервером
На удаленном сервере безопасность контролирует серверная служба пере- направителя MSNP. При попытке получить доступ к защищенному объекту, она использует реквизиты сеанса для создания маркера удаленного доступа. Далее безопасность обеспечивается так же, как и при локальном доступе (рис. 2-3)-

1. Пользователь входит на рабочую станцию А
с реквизитами для домена Windows NT
Контроллер домена
2. Пользовательские реквизиты входа проверяются контроллером домена
Windows NT
Рабочая станция В
Рабочая станция А
4 Перенаправитель
MSNP устанавливает
3. Пользователь запрашивает открытие файла по
UNC-соединению на рабочей станции В
сеанс на основе реквизитов,
полученных на шаге 1
(или специальных реквизитов для удаленного доступа,
предоставленных Windows NT
или Windows 2000)
5 Серверная служба перенаправителя на удаленной системе проверяет реквизиты сеанса на контроллере домена
6. Удаленная система обслуживает UNC-запрос
Рис. 2-3. Применение реквизитов безопасности
Пример
Приложения Win32 могут использовать API-функции CreateFile, ReadFile и
WriteFile для создания, открытия и изменения файлов по сети с использова- нием перенаправителя MSNP. Заметьте, что только платформы Windows NT
и Windows 2000 поддерживают модель безопасности Win32. В листинге 2-1
приведен пример кода приложения, создающего файл по UNC-соединению.
Вы можете найти его в папке \Examples\Chapter02 на прилагаемом компакт- диске.
Листинг 2-1. Простой пример создания файла
«include
((include
void main(void)
HANDLE FileHandle;
DWORD BytesWritten;
// Открытие файла \\Myserver\Myshare\Sample.txt if ((FileHandle = CreateFile("\\\\Myserver\\Myshare\\Sample.txt",
GENERIC_WRITE | GENERIC.READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL))
== INVALID_HANDLE_VALUE)
p r i n t f ( " C r e a t e F i l e f a i l e d with e r r o r Xd\n", GetLastErrorO);
return;

Г Л А В А 2 Перенаправитель 63
Листинг 2-1. (продолжение)
II Запись 14 байт в новый файл if (WriteFile(FileHandle, "This is a test", 14,
&BytesWritten, NULL) == 0)
{
printf("WriteFile failed with error Xd\n", GetLastError());
return;
if (CloseHandle(FileHandle) == 0)
{
printf("CloseHandle failed with error Xd\n", GetLastErrorO);
return;
Резюме
В этой главе вы познакомились с перенаправителем Windows, позволяющим приложениям получать доступ к ресурсам файловой системы Windows по сети. Мы рассказали, как он осуществляет обмен информацией по сети и как при этом используется система безопасности Windows NT и Windows 2000.
Две следующие главы посвящены технологиям почтовых ящиков и име- нованных каналов, опирающихся на перенаправитель для выполнения лю- бых действий по сети.
At
-А„

Г Л А В А
Почтовые ящики
В Microsoft Windows NT, Windows 2000, Windows 95 и Windows 98 (но не
Windows СЕ) реализован простой однонаправленный механизм межпро-
цессной связи (interprocess communication, IPC), называемый почтовыми
ящиками (mailslots). Почтовые ящики позволяют клиентскому процессу пе- редавать сообщения одному или нескольким серверным процессам. Они также помогают передавать сообщения между процессами на одном и том же компьютере или на разных компьютерах в сети. Разработка приложений,
использующих почтовые ящики, не требует знания сетевых транспортных протоколов, таких как TCP/IP или IPX. Поскольку почтовые ящики основа- ны на архитектуре широковещания, они не гарантируют надежной переда- чи данных, но полезны, когда доставка данных не является жизненно важ- ной.
Один из таких случаев — создание системы сообщений, охватывающей всех сотрудников офиса. Допустим, в офисе с множеством рабочих станций не хватает газированной воды, и все пользователи хотят каждые несколько минут знать, сколько банок кока-колы осталось в автомате. Легко установить клиентское приложение, отслеживающее количество банок и передающее эту информацию всем заинтересованным пользователям каждые пять минут.
Поскольку почтовые ящики не гарантируют доставку широковещательного сообщения, некоторые пользователи могут не получить всех обновлений информации. Но несколько неудачных передач не приведут к неприятнос- тям, потому что информация обновляется достаточно часто.
Итак, основное ограничение почтовых ящиков: они допускают только не- надежную однонаправленную передачу данных от клиента к серверу. Основ- ное преимущество: клиентские приложения могут легко посылать широкове- щательные сообщения одному или нескольким серверным приложениям.
В этой главе мы расскажем о создании клиент-серверного приложения,
правилах именования почтовых ящиков, влиянии размера сообщений на ра- боту почтовых ящиков, связанных с ними проблемах и ограничениях.
Подробности внедрения почтовых ящиков
Почтовые ящики основаны на интерфейсе файловой системы Windows. Кли- ентское и серверное приложения используют стандартные функции ввода- вывода файловой системы Win32 (такие как ReadFtte и WriteFile) для отправ- ки и получения данных почтовым ящиком, а также правила именования фай-

Г Л А В А 3 Почтовые ящики 65
ловой системы Win32. Для создания и идентификации почтовых ящиков перенаправитель Windows использует файловую систему Mailslot File System
(MSFS). Подробнее о перенаправителе Windows — в главе 2.
Имена почтовых ящиков
Почтовые ящики именуются по следующему правилу:
\\cepsep\Mailslot\[путь]имя
Строка состоит из трех частей: \\server, \Mailslot и \[path]name, где \\cep-
вер — имя сервера, на котором создается почтовый ящик и выполняется сер- верное приложение; \Mailslot — фиксированная обязательная строка, уведом- ляющая систему, что это имя файла относится к MSFS; \[путь]имя — позволя- ет приложениям уникальным образом определять и идентифицировать имя почтового ящика. В строке путь разрешается задавать несколько уровней ка- талогов. Например, допустимы следующие типы имен почтовых ящиков:
\\Oreo\Mailslot\Mymailslot
\\Testserver\Mailslot\Cooldirectory\Funtest\Anothermailslot
\\.\Mailslot\Easymailslot
\\*\Mailslot\Myslot
Строка сервер может представлять собой точку (.), звездочку (*), имя до- мена или сервера. Домен — это группа рабочих станций и серверов с общим групповым именем. Об именах почтовых ящиков мы расскажем далее в этой главе, когда будем подробно обсуждать реализацию простого клиента.
Поскольку для создания и передачи данных по сети почтовые ящики ис- пользуют службы файловой системы Windows, их интерфейсный протокол независим. При создании приложения, процессы которого взаимодействуют по сети, не нужно знать, как работают нижележащие сетевые транспортные протоколы. Когда почтовые ящики устанавливают удаленное соединение с компьютерами в сети, для передачи данных от клиента к серверу перенапра- витель Windows использует протокол Server Message Block (SMB). Сообщения обычно пересылаются без установления соединения, но в зависимости от размера сообщения вы можете заставить перенаправитель Windows устанав- ливать соединения на компьютерах с Windows NT или Windows 2000.
Размеры сообщений
Для передачи сообщений по сети почтовые ящики обычно используют дейтаг-
раммы (datagram) — небольшие порции данных, передаваемые по сети без ус- тановления соединения. Это ненадежный способ, поскольку уведомление о получении пакета данных не пересылается, и его доставка не гарантируется.
Между тем, с помощью дейтаграмм можно передавать сообщения от одного клиента многим серверам. Этот механизм не работает на компьютерах с Win- dows NT и Windows 2000, если размер сообщения превышает 424 байта.

66
ЧАСТЬ I Устаревшие сетевые API
На компьютерах с Windows NT и Windows 2000 сообщения размером более 426 байт передаются с использованием протокола, требующего уста- новления соединения в сеансе SMB, а не дейтаграммами. Это позволяет пе- редавать большие сообщения надежно и эффективно. Впрочем, в этом слу- чае вы теряете возможность передавать сообщение от одного клиента мно- гим серверам. При установлении соединения допускается соединение толь- ко одного клиента с одним сервером и гарантируется доставка данных меж- ду процессами.
Однако интерфейс почтового ящика в Windows NT и Windows 2000 не гарантирует, что сообщение будет действительно записано в почтовый ящик.
Например, если вы посылаете большое сообщение от клиента несуществую- щему серверу, интерфейс почтового ящика не сообщит клиентскому прило- жению, что не смог передать данные.
Поскольку Windows NT и Windows 2000 выбирают способ передачи в за- висимости от размера сообщения, появляется проблема совместимости при передаче больших сообщений между компьютером с Windows NT или Win- dows 2000 и компьютером с Windows 95 или Windows 98.
Windows 95 и Windows 98 доставляют сообщения только посредством дей- таграмм, независимо от их размеров. Если клиент Windows 95 или Windows
98 попытается отправить сообщение больше 424 байт серверу Windows NT
или Windows 2000, последний примет первые 424 байта и отбросит осталь- ные, поскольку принимает большие сообщения в сеансе SMB с установлени- ем соединения. Похожая проблема существует при передаче сообщений от клиента Windows NT или Windows 2000 серверу Windows 95 или Windows 98.
Помните: Windows 95 и Windows 98 получают данные только посредством дейтаграмм. Поскольку Windows NT и Windows 2000 передают дейтаграмма- ми только сообщения размером не более 426 байт, Windows 95 и Windows 98
не получат от таких клиентов сообщений большего размера (табл. 3-1)-
Табл. 3-1. Ограничения размера сообщений почтовых ящиков
Направление Передача посредством
передачи дейтаграмм без установления
соединения
Передача с установлением
соединения
Windows 95 или
Windows 98
-> Windows 95
или Windows 98
Windows NT или
Windows 2000
-> Windows NT
или Windows 2000
Windows NT или
Windows 2000
-> Windows 95
или Windows 98
Windows 95 или
Windows 98
-> Windows NT
или Windows 2000
Размер сообщения не более 64 кб
Размер сообщения не более 424 байт
Размер сообщения не более 424 байт
Размер сообщения не более
424 байт, иначе сообщение усекается до этого размера
Не поддерживается
Сообщения должны быть более 426 байт
Не поддерживается
Не поддерживается
V.f

Г Л А В А 3 Почтовые ящики 67
ПРИМЕЧАНИЕ Windows СЕ не описана в табл. 3-1, потому что в этой
ОС интерфейс программирования почтовых ящиков не доступен. Со- общения размером 425—426 байт также не описаны в таблице, из-за ограничений перенаправителей Windows NT и Windows 2000.
Перенаправители Windows NT и Windows 2000 не могут отправлять или принимать дейтаграммы размером 425—426 байт. Например, если вы от- правляете от клиента Windows NT или Windows 2000 сообщение серверу
Windows 95, Windows 98, Windows NT или Windows 2000, перед его отправ- кой серверу перенаправитель Windows NT усекает сообщение до 424 байт.
Чтобы обеспечить полную совместимость всех платформ Windows, огра- ничте размер сообщений 424 байтами. При установлении соединений вос- пользуйтесь вместо почтовых ящиков именованными каналами.
Компиляция приложения
При сборке клиента или сервера почтовых ящиков в Microsoft Visual C++
необходимо включать в программные файлы приложений заголовочный файл Winbase.h. Если вы включаете файл Windows.h (как в большинстве при- ложений), Winbase.h можно опустить. Ваше приложение также должно ком- поноваться с библиотекой Kernel32.1ib, соответствующие параметры обычно настраивают с помощью флагов компоновщика Visual C++.
Коды ошибок
Все API-функции Win32, используемые при разработке клиентов и серверов почтовых ящиков (за исключением CreateFile и CreateMailslof), в случае не- удачи возвращают 0. API-функции CreateFile и CreateMailslot возвращают зна- чение INVALID_HANDLE_VALUE. Для получения кодов ошибок этих функций,
приложение должно вызывать функцию GetLastError. Полный список кодов ошибок см. в приложении С или в файле Winerror.h.
Общие сведения об архитектуре клиент-сервер
Почтовые ящики используют простую архитектуру клиент-сервер, в которой данные передаются от клиента серверу однонаправленно. Сервер отвечает за создание почтового ящика и является единственным процессом, который может читать из него данные. Клиенты почтовых ящиков — это процессы,
открывающие экземпляры почтовых ящиков и единственные, имеющие пра- во записывать в них данные.
Сервер почтовых ящиков
Для реализации почтового ящика нужно написать базовое серверное при- ложение, в котором выполнить следующие действия.
• Создать описатель почтового ящика с помощью API-функции CreateMailslot.
Получить данные от любого клиента путем вызова API-функции ReadFile
с описателем почтового ящика в качестве параметра.
Закрыть описатель почтового ящика с помощью API-функции CloseHandle.

68
ЧАСТЬ I Устаревшие сетевые API
Как видно, для разработки серверного приложения почтового ящика тре- буется совсем немного вызовов API-функций.
Серверные процессы создают почтовые ящики с помощью вызова API- функции CreateMailslot, которая определена так:
HANDLE CreateMailslot(
LPCTSTR IpName,
DWORD nMaxMessageSize,
DWORD /ReadTimeout,
LPSECURITY_ATTRIBUTES IpSecurltyAttrlbutes
Параметр IpName задает имя почтового ящика. Оно должно иметь сле- дующий ВИД:
\\.\Mailslot\[путь]имя
Заметьте: имя сервера представлено точкой, что означает локальный ком- пьютер. Это необходимо, поскольку нельзя создать почтовый ящик на уда- ленном компьютере. В параметре IpName имя обязательно уникально, но может быть простым или включать полный путь.
Параметр nMaxMessageSize задает максимальный размер (в байтах) сооб- щения, которое может быть записано в почтовый ящик. Если клиент запи- сывает сообщение большего размера, сервер не видит это сообщение. Если задать значение 0, то сервер будет принимать сообщения любого размера.
Операции чтения (подробнее — далее в этой главе) могут выполняться с блокировкой почтового ящика или без таковой, в зависимости от параметра
IReadTimeout, задающего количество времени (в миллисекундах), в течение ко- торого операции чтения ждут входящих сообщений. Значение MAILSLOT_
WAIT_FOREVER позволит заблокировать операции чтения и заставит их бес- конечно ожидать, пока входящие данные не станут доступны для чтения. Если задать значение 0, операции чтения возвращаются немедленно.
Параметр IpSecurityAttibutes определяет права доступа к почтовому ящи- ку. В Windows 95 и Windows 98 он должен иметь значение NULL, потому что в этих ОС система безопасности к объектам не применима. В Windows NT и
Windows 2000 этот параметр реализован частично, поэтому следует также присвоить ему NULL.
Почтовый ящик относительно безопасен только при локальном вводе- выводе, когда клиент пытается открыть этот ящик, используя точку (.) в ка- честве имени сервера. Клиент может обойти систему безопасности, задав вместо точки фактическое имя севера, как при удаленном вызове ввода-вы- вода. Параметр IpSecurityAttibutes не реализован для удаленного ввода-выво- да в Windows NT и Windows 2000 из-за больших издержек, связанных с фор- мированием аутентифицированного сеанса между клиентом и сервером при каждой отправке сообщения. Таким образом, почтовые ящики только час- тично соответствуют модели безопасности Windows NT и Windows 2000,
реализованной в стандартных файловых системах. В итоге, любой клиент почтового ящика в сети может отправлять данные серверу.

Г Л А В А 3 Почтовые ящики 69
После создания почтового ящика с действительным описателем можно читать данные. Сервер — единственный процесс, который может читать дан- ные из почтового ящика. Для он должен вызвать "№т32-функцию ReadFile,
определенную так:
BOOL ReadFile(
. HANDLE hFile,
LPVOID ipBuffer,
DWORD nNumberOfBytesToRead,
LPDWORD lpNumberOfBytesRead,
LPOVERLAPPED lpOverlapped
);
CreateMailslot возвращает описатель hFile. Параметры IpBuffer и nNum-
berOJBytesToRead определяют, сколько данных может быть считано из почто- вого ящика. Важно задать размер этого буфера большим, чем параметр
nMaxMessageSize из API-вызова CreateMailslot. Кроме того, размер буфера дол- жен превышать размеры входящих сообщений, иначе ReadFile выдаст код ошибки ERROR_INSUFFICIET_BUFFER. Параметр IpNumberOJBytes возвращает количество считанных байтов по завершении работы ReadFile.
Параметр lpOverlapped позволяет считывать данные из почтового ящика асинхронно. Он использует Win32-MexaHH3M перекрытого ввода-вывода
(overlapped I/O), подробно описанный в главе 4. По умолчанию, выполнение
ReadFile блокирует (ожидает) ввод-вывод, пока данные не станут доступны для чтения. Перекрытый ввод-вывод применим только в Windows NT и Win- dows 2000, в Windows 95 или Windows 98 присвойте параметру lpOverlapped
значение NULL. В листинге 3-1 показано, как написать простой сервер почто- вых ящиков.
Листинг 3-1. Пример сервера почтовых ящиков
// Served.срр
«include
«include
void main(void)
{
HANDLE Mailslot;
char buffer[256];
DWORD NumberOfBytesRead;
// Создание почтового ящика
if ((Mailslot = CreateMailslot("\\\V\\Mailslot\\Myslot", o,
MAILSLOT_WAIT_FOREVER, NULL)) == INVALID_HANDLE_VALUE)
{
printf("Failed to create a mailslot Xd\n", GetLastErrorO);
см.след.стр.

-1МЧ* i o i устаревшие сетевые API
Листинг 3-1. (продолжение)
/I Бесконечное чтение данных из почтового ящика while(ReadFile(Mailslot, buffer, 256, &NumberOfBytesRead,
NULL) i= 0)
{
p n n t f ( X *s\n , NumberOfBytesRead, buffer),
Клиент почтовых ящиков
Для реализации клиента нужно разработать приложение, ссылающееся на существующий почтовый ящик и записывающее в него данные В базовом клиентском приложения необходимо выполнить следующие шаги
1 Открыть описатель-ссылку на почтовый ящик, в который нужно отпра- вить данные, с помощью API-функции CreateFile
2 Записать данные в почтовый ящик, вызвав API-функцию WnteFile
3 Закрыть описатель почтового ящика с помощью API-функции CloseHandle
Как уже говорилось, клиенты почтовых ящиков соединяются с сервера- ми без установления соединения Когда клиент открывает описатель-ссыл- ку на почтовый ящик, он не устанавливает связь с сервером почтового ящи- ка На почтовые ящики ссылаются путем вызова API-функции CreateFile, оп- ределенной так
HANDLE CreateFile(
LPCTSTR lpFlleName,
DWORD dwDeslredAccess,
DWORD dwShareMode,
LPSECURITY.ATTRIBUTES lpSecurityAttributes,
DWORD dwCreationDisposltion,
DWORD dwFlagsAndAttrlbutes,
HANDLE hTemplateFlle
),
Параметр lpFlleName описывает один или несколько почтовых ящиков, в которые можно поместить данные Для этого укажите имя ящика в одном из форматов, описанных в этой главе Правила именования почтовых ящиков таковы
И \\.\mailslot\«umi — определяет локальный почтовый ящик на том же компьютере,
Ш \\wmi_cepeepa\mailslot\uMn определяет удаленный сервер почтово- го ящика с именем имя_сервера,
Ш \\имя_домена\та.Ив\о?\имя — определяет все почтовые ящики с име- нем имя в домене имя_домена,
Ш \V\mailslot\ii)»wi — определяет все почтовые ящики с именем имя в ос- новном домене системы
jl A DM J I ЮЧТОВЫе ЯЩИКИ И
Параметр dwDestredAccess должен иметь значение GENERICJWR1ТЕ, пото- му что клиент может только записывать данные на сервер Параметр dwS-
hareMode обязан иметь значение FILE SHARE_READ, позволяя серверу откры- вать и выполнять операции чтения из почтового ящика Значение параметра
ipSecuntyAttnbutes не влияет на почтовые ящики — следует задать A
7
LL Флаг
dwCreationDisposition должен быть равен OPEN_EXISTING Это удобно, ког/
клиент и сервер функционируют на одном и том же компьютере Если се^
вер не создал почтовый ящик, API-функция CreateFile вернет ошибку Пара- метр dwCreationDisposition не имеет значения, если сервер работает уд щен- но Параметр divFlagsAndAttnbutes должен иметь значение FILE_ ATTRIBU-
TE NORMAL, a hTemplateFile — значение NULL
После успешного создания описателя можно помещать данные в почто- вый ящик Помните, что клиент может только записывать данные в почто- вый ящик с помощью Win32^yHK4HH WnteFile
BOOL WnteFile(
HANDLE hFlle,
LPCVOID lpBuffer,
DWORD nNumberOfBytesToWnte,
LPDWORD lpNumberOfBytesWntten,
LPOVERLAPPED lpOverlapped
),
Параметр hFile — это описатель-ссылка, возвращаемый функцией Create-
File Параметры lpBuffer и nNumberOJBytesToWnte определяют, сколько байт будет отправлено от клиента серверу Максимальный размер сообщения —
64 кб Если описатель почтового ящика создан с указанием домена или звез- дочки, размер сообщения не должен превышать 424 байта в Windows NT и
Windows 2000, или 64 кб — Windows 95 и Windows 98 Если клиент попы- тается отправить сообщение большего размера, функция WnteFile вернет ошибку ERROR_BAD_NETPATH (чтобы узнать код ошибки, вызовите функцию
GetLastErrof) Это происходит потому, что сообщение посылается всем сер- верам сети как широковещательная дейтаграмма Параметр ipNumberOf-
BytesWritten возвращает количество байт, отправленных серверу после завер- шения функции WnteFile
Параметр lpOverlapped позволяет записывать данные в почтовый ящик асинхронно Поскольку почтовые ящики обмениваются данными без уста- новления соединения, функция WnteFile не блокирует ввод-вывод На клиен- те этот параметр должен быть равен NULL В листинге 3-2 приведен пример простого клиента почтового ящика
Листинг 3-2. Пример клиента почтового ящика
// Client cpp
"include
«include
m n ( l n t argc, char .argv[]>
ш с/шд

72 ЧАСТЬ I Устаревшие сетевые API
Листинг 3-2. {продолжение)
{ ,
HANDLE Mailslot;
DWORD BytesWritten;
CHAR ServerName[256];
// Ввод аргумента командной строки для сервера, которому отправляется сообщение
if (argc < 2)
{
printf("Usage: client \n");
return;
}
sprintf(ServerName, "\\\\Xs\\Mailslot\\Myslot", argv[1]);
if ((Mailslot = CreateFile(ServerName, GENERIC_WRITE,
FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
NULL)) == INVALID_HANDLE_VALUE)
{
printf("CreateFile failed with error Xd\n", GetLastErrorO);
return;
if ( W r i t e F i l e ( M a i l s l o t , "This is a t e s t " , 14, &BytesWritten,
NULL) == 0)
{
p r i n t f ( " W r i t e F i l e f a i l e d with error Xd\n", GetLastErrorO);
return;
printf("Wrote Xd bytes\n", BytesWritten);
CloseHandle(Mailslot);
}
Дополнительные API-функции
почтовых ящиков
Сервер почтового ящика может использовать две дополнительные API-фун- кции для взаимодействия с почтовым ящиком: GetMailslotlnfo и SetMailslotlnfo.
Функция GetMailslotlnfo возвращает размер сообщения, когда оно прибыва- ет в почтовый ящик. Приложения используют эту возможность, чтобы дина- мически настраивать свои буферы для входящих сообщений изменяющей- ся длины. GetMailslotlnfo может также применяться для опроса наличия вхо- дящих данных:
BOOL GetMailslotInfo(
HANDLE hMailslot,
LPDWORD lpMaxMessageSize,
LPDWORD lpNextSize, '*"
ПТ
' ^'<«-»• "»«*'**»••**

Г Л А В А 3 Почтовые ящики 73
LPDWORD lpMessageCount,
LPDWORD lpReadTimeout
);
Параметр hMailslot указывает почтовый ящик, возвращенный вызовом др!_функции CreateMailslot. Параметр ipMaxMessageSize задает, сообщение какого размера (в байтах) можно записать в почтовый ящик. Параметр 1р-
NextSize указывает на размер следующего сообщения (в байтах). Параметр
GetMailslotlnfo может вернуть значение MAILSLOT_NO_MESSAGE, указывая, что в настоящий момент почтовый ящик не ждет никакого сообщения. Потен- циально сервер вправе использовать этот параметр для проверки наличия входящих данных, не давая приложению блокировать ввод-вывод при вызо- ве функции ReadFile. Но делать это не рекомендуется: приложение будет не- прерывно использовать центральный процессор для проверки входящих данных, даже если не обрабатываются никакие сообщения, что уменьшит производительность.
Если вы хотите предотвратить блокирование при вызове функции Rea-
dFile, используйте перекрытый ввод-вывод Win32. Параметр IpMesssageCount
задает буфер, куда записывается общее количество сообщений, ожидающих прочтения. Этот параметр также задействуют для проверки наличия данных.
Параметр lpReadTimeout указывает на буфер, возвращающий время тайм- аута (в миллисекундах), в течение которого операция чтения ждет записи со- общения в почтовый ящик.
API-функция SetMailslotlnfo задает значение тайм-аута для почтового ящи- ка, в течение которого операция чтения ожидает входящих сообщений. Та- ким образом, приложение может изменить способ чтения от блокирующе- го к неблокирующему или наоборот. Параметр SetMailslotlnfo определен так:
BOOL SetMailslotInfo(
HANDLE hMailslot,
DWORD IReadTlmeout
);
Параметр hMailslot указывает почтовый ящик, возвращаемый вызовом API- функции CreateMailsot. Параметр IReadTimeout определяет количество време- ни (в миллисекундах), в течение которого операция чтения ожидает записи сообщения в почтовый ящик. Если оно равно 0, то в отсутствие сообщений операции чтения возвращаются немедленно, если MA1LSLOT_WAIT_FOREVER —
будут ждать бесконечно долго.
Платформа и производительность
Почтовые ящики в Windows 95 и Windows 98 имеют ограничения: они име- нуются по правилу «8.3», не позволяют отменить блокирующие запросы вво-
Да-вывода, вызывают утечки памяти по истечении тайм-аута.
Правила именования «8.3»
indows 95 и Windows 98 ограничивают размеры имен почтовых ящиков форматом «8.3». Это создает проблемы совместимости между Windows 95

74 ЧАСТЬ I Устаревшие сетевые API
или Windows 98 и Windows NT или Windows 2000 Например, если вы попы- таетесь создать или открыть почтовый ящик с именем \\ \Mailslot\Mymailslot,
Windows 95 создаст почтовый ящик \\ \Mailslot\Mymailsl и будет ссылаться на него Функции CreateMmlslot и CreateFile выполнятся успешно, несмотря на усечение имени Если затем вы отправите сообщение от Windows 2000 к
Windows 95 или наоборот, оно не будет получено из-за несоответствия имен почтовых ящиков Если клиент и сервер работают на компьютерах с Win- dows 95, проблем не возникает — имя усекается как на клиенте, так и на сер- вере Чтобы избежать осложнений, не создавайте почтовые ящики с длин- ными именами
Неспособность отменить блокирующие запросы
ввода-вывода
Эта проблема существует в Windows 95 и Windows 98 Серверы почтовых ящиков для получения данных вызывают функцию ReadFile Если почтовый ящик создается с флагом MAILSLOT_WAIT_FOREVER, запросы блокируются на неопределенное время, пока данные не станут доступны При невыполнен- ном запросе функции ReadFile серверное приложение при завершении за- висает Единственный способ снять приложение — перезагрузить Windows
Для решения этой проблемы заставьте сервер открыть описатель его почто- вого ящика в отдельном потоке и отправить данные, чтобы прервать блоки- рующий запрос чтения (см листиш 3-3)
Листинг 3-3. Исправленный сервер почтовых ящиков
// Serveг2 срр
((include
«include
«include
BOOL StopProcessmg,
DWORD WINAPI ServeMailslot(LPVOID lpParameter),
void SendMessageToMallslot(void),
void raam(void) {
DWORD Threadld,
HANDLE MailslotThread,
StopProcessmg = FALSE,
MailslotThread = CreateThread(NULL, 0, ServeMailslot, NULL,
0, &ThreadId),
i M i ] itji
printf( Press a key to stop the server\n );_, „
_getch(), Б.8» I*
// Флагу StopProcessmg присваивается TRUE/'чТООМ при завершении

Г Л А В А 3 Почтовые ящики
75
Листинг 3-3. (продолжение)
Ц функции ReadFile поток сервера закончился
StopProcessing = TRUE,
// Отправка сообщения почтовому ящику, чтобы прервать
// вызов функции ReadFile на сервере
"* SendMessageToMailslotO,
// Ожидание завершения выполнения потока сервера if (WaitForSingleObject(MailslotThread, INFINITE) == WAIT_FAILED)
{
pnntf( WaitForSingleOb]ect failed with error Xd\n ,
GetLastErrorO),
return,
// Функция ServeMailslot
//
// Описание
// Эта рабочая функция сервера почтового ящика
// для обработки всего входящего ввода-вывода ящика
//
DWORD WINAPI ServeMailslot(LPVOID lpParameter)
{
char buffer[2048],
DWORD NumberOfBytesRead,
DWORD Ret,
HANDLE Mailslot,
if ((Mailslot = CreateMailslot( \\\\ \\mailslot\\myslof, 2048,
MAILSLOT_WAIT_FOREVER, NULL)) == INVALID_HANDLE_VALUE)
{
pnntf( Failed to create a MailSlot Xd\n , GetLastErrorO);
return 0,
while((Ret = ReadFile(Mailslot, buffer, 2048,
&NumberOfBytesRead, NULL)) i= 0)
{
if (StopProcessing)
break,
printf( Received %d bytes\n , NumberOfBytesRead);
CloseHandle(Mailslot),
см след стр

76
ЧАСТЬ I Устаревшие сетевые API
Листинг 3-2. (продолжение)
return 0;
>
// Функция SendMessageToMailslot
//
// Описание.
// Функция SendMessageToMailslot отправляет простое сообщение
// серверу, чтобы прервать блокирующий вызов API-функции ReadFile
//
void SendMessageToMailslot(void)
{
HANDLE Mailslot;
DWORD BytesWritten;
if ((Mailslot = CreateFile("\\\\.\\mailslot\\inyslot",
GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, NULL)) == INVALID_HANDLE_VALUE)
{
printf("CreateFile failed with error Kd\n", GetLastErrorO);
return;
}
if (WnteFile(Mailslot, "STOP", 4, &BytesWntten, NULL) == 0)
{
pnntf("WriteFile failed with error %d\n", GetLastErrorO);
return;
CloseHandle(Mailslot);
Утечки памяти
/V )1Ol9l£i <
Утечки памяти в Windows 95 и Windows 98 происходят при использовании значений тайм-аута в почтовых ящиках. Если почтовый ящик создается функцией CreateMailslot со значением тайм-аута, большим 0, функция Read-
File вызывает утечку памяти, когда время ожидания истекает, и функция воз- вращает FALSE. После многократных вызовов функции ReadFile система ста- новится нестабильной и последующие вызовы этой функции, время ожида- ния которых истекает, начинают возвращать TRUE. В результате система больше не может выполнять другие MS-DOS-приложения Для решения этой проблемы создавайте почтовый ящик со временем ожидания, равным 0 или
MAILSLOT_WAIT_FOREVER. Это не позволит приложению использовать меха- низм тайм-аутов, вызывающий утечки памяти.
В базе знаний Microsoft, к которой можно обратиться по адресу http //
support.microsoft.com/support/search, описаны следующие проблемы и огра- ничения.

Г Л А В А 3 Почтовые ящики 77
Я Q139715 — функция ReadFile возвращает неверный код о ш и б к и
для почтовых ящиков. Когда сервер открывает почтовый ящик функ- цией CreateMmlslot, задает тайм-аут и затем получает данные функцией
ReadFile, последняя выдает ошибку 5 (доступ отклонен), если данные не- доступны.
Ш Q192276 функция GetMailslotlnfo возвращает неверное значение
параметра ipNextSize. Если вызвать эту API-функцию в Windows 95 OEM
Service Release 2 (OSR2) или Windows 98 без установки компонента сете- вого клиента, вы получите неправильное значение (обычно в миллионах)
или отрицательное число для параметра IpNextSize Если вызвать функцию повторно, она обычно начинает возвращать правильное значение.
• Q170581 — почтовый ящик, созданный в Windows 95, вмещает толь-
ко 4093 байта. Вызов API-функции WriteFile для записи более 4093 байт в почтовый ящик, созданный на рабочей станции Windows 95, не заверша- ется успешно.
Ш Q131493 — функция CreateFile и почтовые ящики. В документации на API-функцию CreateFile неверно описаны возможные значения, возвра- щаемые ею при открытии клиентской части почтового ящика
Резюме
Сетевая технология почтовых ящиков предоставляет приложениям простой однонаправленный механизм межпроцессной связи с использованием пе- ренаправителя Windows. Наиболее полезная возможность — широковеща- тельная передача сообщений одному или нескольким компьютерам в сети.
Впрочем, почтовые ящики не обеспечивают надежную передачу данных.
Если вы хотите надежно передавать данные с помощью перенаправите - ля Windows, используйте именованные каналы, которым посвящена следую- щая глава.

Г Л А В А
Именованные каналы
Именованные каналы — это простой механизм связи между процессами
(interprocess communication, IPC), поддерживаемый в Microsoft Windows NT,
Windows 2000, Windows 95 и Windows 98 (но не Windows СЕ) Именованные каналы обеспечивают надежную одностороннюю и двустороннюю переда- чу данных между процессами на одном или разных компьютерах Разработка приложений, работающих с именованными каналами, не представляет слож- ности и не требует особых знаний механизма работы основных сетевых протоколов (таких как TCP/IP или IPX) Детали работы протоколов скрыты от приложения, так как для обмена данными между процессами через сеть именованные каналы используют перенаправитель сети Microsoft — Mic- rosoft Network Provider (MSNP) Очень важно и то, что именованные кана- лы позволяют воспользоваться встроенными возможностями защиты Win- dows NT или Windows 2000
К примерам использования именованных каналов можно отнести разра- ботку системы управления данными, которая позволяет выполнять транзак- ции только определенной группе пользователей Рассмотрим следующую си- туацию в офисе есть компьютер с некоей секретной информацией, доступ к которой должен иметь только управленческий персонал Допустим, каж- дый сотрудник фирмы должен видеть этот компьютер со своей рабочей станции, однако простые служащие не вправе иметь доступ к конфиденци- альным данным Эта проблема решается с помощью именованных каналов
Можно разработать серверное приложение, которое будет выполнять тран- закции над секретными данными в зависимости от запросов клиентов С
помощью встроенных возможностей защиты Windows NT или Windows
2000 сервер ограничит доступ рядовых сотрудников к конфиденциальным данным
Именованные каналы представляют собой простую архитектуру клиент- сервер, обеспечивающую надежную передачу данных Эта глава посвящена разработке сервера и клиента именованного канала Мы начнем с обсужде- ния названий и типов простых именованных каналов Затем создадим про- стой сервер и рассмотрим детали реализации более сложного сервера Пос- ле этого разберем создание простого клиента именованного канала В кон- це главы будут перечислены основные проблемы и ограничения, связанные с именованными каналами

Г Л А В А 4 Именованные каналы /ы
Детали реализации именованных каналов
Для работы с файловой системой Windows именованные каналы использу- ют интерфейс Named Pipe File System (NPFS) Для получения и отправки дан- ных сервер и клиент применяют стандартные API-функции Win32, таки^ как
ReadFile и WnteFile Эти функции позволяют приложениям использовать правила именования файловой системы Win32 и возможности защиты Win- dows NT и Windows 2000 При отправке и получении данных по сети интер- фейс NPFS задействует перенаправитель MSNP Это делает его независимым от протокола при разработке приложений программист не заботится о де- талях работы протоколов (TCP/IP или IPX) Названия именованных каналов должны удовлетворять формату Universal Naming Convention (UNC) Подроб- нее о UNC, перенаправителе Windows и безопасности — в главе 2
Правила именования каналов
Имена каналов имеют следующий формат
\\сервер\Р1ре\[луть]имя
Данная строка состоит из трех частей \\сервер, \Pipe и \[путь]имя, где
\\сервер — имя сервера, на котором создан именованный канал, \Pipe —
фиксированная обязательная строка, уведомляющая систему о принадлеж- ности к NPFS, \[путъ]имя — уникальное имя канала, включающее несколько уровней каталогов Вот примеры правильных названий именованных кана- лов
\\myse rve r\PIPE\mypipe
\\Testserver\pipe\cooldirectory\funtest\jim
\\ \Pipe\Easynamedpipe
Имя сервера может быть представлено точкой
Режимы побайтовый и сообщений
Именованные каналы используют два режима передачи данных побайтовый и сообщений В первом случае сообщения передаются непрерывным пото- ком байтов между клиентом и сервером Это означает, что клиент и сервер точно не знают, сколько байтов считывается или записывается в канал в оп- ределенный момент времени Таким образом, запись одного количества бай- тов не означает чтение того же количества с другой стороны канала Такой способ передачи позволяет клиенту и серверу не заботиться о содержимом передаваемых данных Во втором случае клиент и сервер отправляют и при- нимают данные дискретными блоками, при этом каждое сообщение прочи- тывается целиком (рис 4-1)
Компиляция приложений
фи создании клиента или сервера именованного канала с помощью Mic- rosoft Visual C++ необходимо включить в программные файлы заголовочный

80
ЧАСТЬ I Устаревшие сетевые API
файл Winbase.h. Если приложение включает файл Windows.h (как правило,
это так), файл Winbase.h можно опустить. Кроме того, как уже упоминалось,
с помощью флагов компоновщика Visual C++ необходимо подключить биб- лиотеку Kernel32.1ib.
Клиент
Побайтовый режим
Сообщение 1 Сообщение 2 Сообщение 3
I I I I I I I I I
Режим сообщений
Сервер
Рис. 4-1. Режимы побайтовый и сообщений
Коды ошибок
Все API-функции Win32 (кроме CreateFile и CreateNamedPipe'), используемые при разработке сервера и клиента именованного канала, при возникнове- нии ошибки возвращают значение 0. Функции CreateFile и CreateNamedPipe
возвращают значение INVALID_HANDLE_VALUE. Подробную информацию об ошибке можно получить с помощью функции GetLastError. Полный список кодов ошибок — в файле Winerror.h или в приложении В.
Простой сервер и клиент
Именованные каналы имеют простую архитектуру клиент-сервер, при кото- рой данные передаются в одном или двух направлениях. Это позволяет и серверу, и клиенту отправлять и принимать данные. Основное отличие сер- вера от клиента — только сервер может создать именованный канал и при- нять соединение с клиентом. Клиентское приложение устанавливает соеди- нение с существующим сервером. После этого сервер и клиент могут читать и записывать данные в канал с помощью стандартных API-функций Win32,
таких как ReadFile и WriteFile. Сервер именованного канала работает только на компьютере с Windows NT или Windows 2000, Windows 95 и Windows 98
не поддерживают создание именованных каналов. Это ограничение не по- зволяет двум компьютерам Windows 95 или Windows 98 напрямую обмени- ваться данными, однако их клиенты могут подключаться к серверам на ком- пьютерах Windows NT и Windows 2000.
Детали реализации сервера
Реализация сервера именованного канала подразумевает разработку прило- жения, создающего экземпляры каналов, к которым подключаются клиенты.

Г Л А В А 4 Именованные каналы 81
Для сервера экземпляр именованного канала — это просто описатель, с по- мощью которого устанавливается соединение с локальными или удаленны- ми клиентами. Процесс создания простого сервера заключается в последо- вательном использовании API-функций:
Ш CreateNamedPipe для создания экземпляра именованного канала;
S CottnectNamedPipe — для прослушивания клиентских соединений;
Ш ReadFile и WriteFile для получения и отправки данных;
Ш DisconnectNamedPipe — для завершения соединения;
Ш CloseHandle — для закрытия описателя экземпляра именованного канала.
Сначала сервер должен создать экземпляр именованного канала с помо- щью API-функции CreateNamedPipe:
HANDLE CreateNamedPipe(
LPCTSTR lpName,
DWORD dwOpenHode,
DWORD dwPipeHode,
DWORD nMaxInstances,
DWORD nOutBufferSize,
DWORD nlnBufferSlze,
DWORD nDefaultTimeOut,
LPSECURITY_ATTRIBUTES ipSecurityAttributes
);
Параметр lpName определяет название именованного канала, удовлетво- ряющее формату UNQ
\\.\Pipe\[путь]имя
Имя сервера представлено точкой — значит, в этом качестве использует- ся локальный компьютер. Именованный канал нельзя создать на удаленном компьютере. Часть параметра [путъ\имя определяет уникальное имя кана- ла. Это может быть просто имя файла или полный путь к нему.
Параметр dwOpenMode определяет направление передачи, управление вводом-выводом и безопасность канала. В табл. 4-1 перечислены флаги, ком- бинации которых используют при создании канала.
Табл. 4-1. Флаги режимов создания именованного канала
Режим Флаг Описание
открытия
Направленный PIPE_ACCESS_DUPLEX Канал двунаправленный: серверные и клиентские процессы могут принимать и отправлять данные по каналу
PIPE ACCESS OUTBOUND Данные передаются только от сервера к клиенту
PIPE_ACCESS_INBOUND Данные передаются только от клиента к серверу
см. след. стр.

82
1   ...   4   5   6   7   8   9   10   11   ...   50


написать администратору сайта