Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Программирование в сетях Windows

.pdf
Скачиваний:
538
Добавлен:
11.03.2015
Размер:
3.02 Mб
Скачать

Г Л А В А

Именованные каналы

Именованные каналы — это простой механизм связи между процессами (interprocess communication, IPC), поддерживаемый в Microsoft Windows NT, Windows 2000, Windows 95 и Windows 98 (но не Windows СЕ) Именованные каналы обеспечивают надежную одностороннюю и двустороннюю передачу данных между процессами на одном или разных компьютерах Разработка приложений, работающих с именованными каналами, не представляет сложности и не требует особых знаний механизма работы основных сетевых протоколов (таких как TCP/IP или IPX) Детали работы протоколов скрыты от приложения, так как для обмена данными между процессами через сеть именованные каналы используют перенаправитель сети Microsoft — Microsoft Network Provider (MSNP) Очень важно и то, что именованные каналы позволяют воспользоваться встроенными возможностями защиты Windows NT или Windows 2000

К примерам использования именованных каналов можно отнести разработку системы управления данными, которая позволяет выполнять транзакции только определенной группе пользователей Рассмотрим следующую ситуацию в офисе есть компьютер с некоей секретной информацией, доступ к которой должен иметь только управленческий персонал Допустим, каждый сотрудник фирмы должен видеть этот компьютер со своей рабочей станции, однако простые служащие не вправе иметь доступ к конфиденциальным данным Эта проблема решается с помощью именованных каналов Можно разработать серверное приложение, которое будет выполнять транзакции над секретными данными в зависимости от запросов клиентов С помощью встроенных возможностей защиты Windows NT или Windows 2000 сервер ограничит доступ рядовых сотрудников к конфиденциальным данным

Именованные каналы представляют собой простую архитектуру клиентсервер, обеспечивающую надежную передачу данных Эта глава посвящена разработке сервера и клиента именованного канала Мы начнем с обсуждения названий и типов простых именованных каналов Затем создадим простой сервер и рассмотрим детали реализации более сложного сервера После этого разберем создание простого клиента именованного канала В конце главы будут перечислены основные проблемы и ограничения, связанные с именованными каналами

Г Л А ВА 4 Именованные каналы

Детали реализации именованных каналов

Для работы с файловой системой Windows именованные каналы используют интерфейс Named Pipe File System (NPFS) Для получения и отправки данных сервер и клиент применяют стандартные API-функции Win32, таки^ как ReadFile и WnteFile Эти функции позволяют приложениям использовать правила именования файловой системы Win32 и возможности защиты Windows NT и Windows 2000 При отправке и получении данных по сети интерфейс NPFS задействует перенаправитель MSNP Это делает его независимым от протокола при разработке приложений программист не заботится о деталях работы протоколов (TCP/IP или IPX) Названия именованных каналов должны удовлетворять формату Universal Naming Convention (UNC) Подробнее о UNC, перенаправителе Windows и безопасности — в главе 2

Правила именования каналов

Имена каналов имеют следующий формат \\сервер\Р1ре\[луть]имя

Данная строка состоит из трех частей \\сервер, \Pipe и \[путь]имя, где \\сервер — имя сервера, на котором создан именованный канал, \Pipe — фиксированная обязательная строка, уведомляющая систему о принадлежности к NPFS, \[путъ]имя — уникальное имя канала, включающее несколько уровней каталогов Вот примеры правильных названий именованных каналов

\\myserver\PIPE\mypipe

\\Testserver\pipe\cooldirectory\funtest\jim

\\\Pipe\Easynamedpipe

Имя сервера может быть представлено точкой

Режимы побайтовый и сообщений

Именованные каналы используют два режима передачи данных побайтовый и сообщений В первом случае сообщения передаются непрерывным потоком байтов между клиентом и сервером Это означает, что клиент и сервер точно не знают, сколько байтов считывается или записывается в канал в определенный момент времени Таким образом, запись одного количества байтов не означает чтение того же количества с другой стороны канала Такой способ передачи позволяет клиенту и серверу не заботиться о содержимом передаваемых данных Во втором случае клиент и сервер отправляют и принимают данные дискретными блоками, при этом каждое сообщение прочитывается целиком (рис 4-1)

Компиляция приложений

фи создании клиента или сервера именованного канала с помощью Microsoft 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:

HANDLECreateNamedPipe(

LPCTSTRlpName,

DWORDdwOpenHode,

DWORDdwPipeHode, DWORDnMaxInstances, DWORDnOutBufferSize, DWORDnlnBufferSlze, DWORDnDefaultTimeOut,

LPSECURITY_ATTRIBUTES ipSecurityAttributes

);

Параметр lpName определяет название именованного канала, удовлетворяющее формату UNQ

\\.\Pipe\[путь]имя

Имя сервера представлено точкой — значит, в этом качестве используется локальный компьютер. Именованный канал нельзя создать на удаленном компьютере. Часть параметра [путъ\имя определяет уникальное имя канала. Это может быть просто имя файла или полный путь к нему.

Параметр dwOpenMode определяет направление передачи, управление вводом-выводом и безопасность канала. В табл. 4-1 перечислены флаги, комбинации которых используют при создании канала.

Табл. 4-1. Флаги режимов создания именованного канала

Режим

Флаг

Описание

открытия

 

 

Направленный

PIPE_ACCESS_DUPLEX

Канал двунаправленный: серверные и

 

 

клиентские процессы могут принимать

 

 

и отправлять данные по каналу

 

PIPE ACCESS OUTBOUND

Данные передаются только от сервера к

 

 

клиенту

 

PIPE_ACCESS_INBOUND

Данные передаются только от клиента к

 

 

серверу

см. след. стр.

82

ЧАСТЬ I Устаревшие сетевые API

 

Табл. 4-1.

(продолжение)

 

Режим

 

Флаг

Описание

открытия

 

 

 

Управление

FILEFLAGJWRITE_

Функции записи не возвращают значе-

вводом-

 

THROUGH

ние, пока данные передаются по сети

выводом

 

 

или находятся в буфере удаленного ком-

 

 

 

пьютера. Применяется только для име-

 

 

 

нованных каналов, работающих в по-

 

 

 

байтовом режиме

 

 

FILEFLAGOVERLAPPED Позволяет использовать перекрытый

 

 

 

ввод-вывод при выполнении операций

 

 

 

чтения, записи и соединения

Безопасность

WRITE DAC

Позволяет приложению изменять

 

 

 

список DACL именованного канала

 

 

ACCESSSYSTEM^SECURITYПозволяетприложениюизменять

 

 

 

список SACL именованного канала

 

 

WRITE OWNER

Позволяет приложению изменять

 

 

 

владельца именованного канала и

 

 

 

групповой SID

Флаги PIPE_ACCESS_ определяют направление передачи данных между сервером и клиентом. Если открыть канал с флагом PIPE^ACCESS_DUPLEX, передача по нему будет двунаправленной. При открытии канала с флагом Р1РЕ_ ACCESSJNBOUND или PIPE_ACCESS_OUTBOUND — однонаправленной: данные будут передаваться только от клиента серверу или наоборот. На рис. 4-2 изображены комбинации флагов и направление передачи между сервером и клиентом.

PIPE_ACCESSJ)UPLEX

 

GENERIC READI

Двунаправленная

 

GENERICIWRITE

 

передача

 

PIPE_ACCESS_OUTBOUND

Однонаправленная

GENERIC_READ

 

передача

 

 

PIPE_ACCESSJI\IBOUND

 

GENERIC_WRITE

Однонаправленная

 

 

 

передача

 

Сервер

Клиент

Рис. 4-2. Флаги режимов и направление передачи

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

Г Л А ВА 4 Именованные каналы

83

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

Последняя группа флагов — dwOpenMode (табл. 4-1), управляет доступом сервера к дескриптору безопасности, созданному именованным каналом. Их ьспользуют, если приложению требуется обновить или изменить дескриптор после создания канала. Флаг WRITE DAC позволяет приложению обновлять список избирательного управления доступом (discretionary access control list, DACL); флаг ACCESS SYSTEM_SECURITY — получить доступ к системному списку управления доступом (system access control list, SACL); флаг WRITEJOWNER — изменять владельца именованного канала и групповой идентификатор безопасности (security ID, SID). Например, можно запретить доступ пользователя к именованному каналу, изменив список DACL канала с помощью API-функций. Подробнее о DACL, SACL и SID — в главе 2.

Параметр dwPipeMode определяет режимы операций чтения, записи и ожидания. При создании канала нужно указать по одному флагу из каждой категории, объединив их с помощью операции OR (табл. 4-2).

Табл. 4-2. Флаги режимов чтения-записи именованного канала

Режим

Флаг

Описание

открытия

 

 

 

Запись

PIPE_TYPE BYTE

Данные записываются в канал потоком

 

 

байтов

 

PIPE_TYPE MESSAGE

Данные записываются в канал потоком

 

 

сообщений

Чтение

PIPE_READMODE_BYTE

Данные считываются из канала потоком

 

 

байтов

 

PIPE_READMODE_

Данные считываются из канала потоком

 

MESSAGE

сообщений

Ожидание

PIPEWAIT

Включен режим блокировки

 

PIPENOWAIT

Отключен режим блокировки

Если при создании побайтового канала использовались флаги PIPE_READMODEJBYTE | PIPE_TYPE_BYTE, то данные будут записываться и считываться из канала только потоком байтов. В этом случае не обязательно уравновешивать количество операций чтения и записи, поскольку данные не разделяются на отдельные сообщения. Например, если в канал записано 500 байт, получатель может считывать по 100 байт, до тех пор пока не считает все данные.

Чтобы сообщения имели четкие границы, создайте канал в режиме сообщений, указав флагиPIPE_READMODE_MESSAGE\PIPE_TYPE_MESSAGE. В этом случае необходимо уравновешивать количество операций чтения и записи Данных. Например, если в канал записано 500 байт, то для чтения данных Потребуется буфер размером 500 байт или более, иначе функция ReadFile

выдаст ошибку ERROR_MORE_DAТА, Флаг PIPE_TYPE_MESSAGE можно комбинировать с флагом PIPE_READMODE_BYTE. Это позволит отправлять данные

84 ЧАСТЬ I Устаревшие сетевые API

в режиме сообщений, а при их получении — считывать произвольное количество байтов. При таком способе передачи разделители сообщений игнорируются. Флаг PIPEJTYPE BYTE нельзя использовать с флагом PIPEREADMODE_MESSAGE, иначе функция CreateNamedPipe выдаст ошибку ERROR_ INVALID PARAMETER, посколькупритакомспособезаписи потокданныхне будет содержать разделителей сообщений.

Флаги PIPE_WAITи PIPE_NOWAIT переводят канал в блокирующий и неблокирующий режим соответственно. Их можно комбинировать с флагами режима чтения и записи. В режиме блокировки такие операции ввода-выво- да, как ReadFile, блокируются, до тех пор пока запрос ввода-вывода не будет выполнен полностью. Такое поведение является стандартным, когда не указанно ни одного флага. Если режим блокировки отключен, операции вводавывода возвращают значение немедленно. Однако данный режим не следует использовать для достижения асинхронного выполнения операций вво- да-вывода в приложениях Win32. Он обеспечивает лишь совместимость с ранними версиями Microsoft LAN Manager 2.0. Асинхронное выполнение функций ReadFile и WriteFile достигается с помощью перекрытого ввода-вывода.

Параметр nMaxInstances определяет максимальное количество экземпляров (описателей) канала, которые одновременно может создать сервер. Экземпляр канала — это соединение локального или удаленного клиента с сервером именованного канала. Параметр может принимать значения от 1 до PIPEJJNLIMITEDJNSTANCES.Например,еслисервердолженодновременно поддерживать до пяти соединений, присвойте параметру значение 5- Если параметрравенPIPEJJNLIMITEDJNSTANCES,количествоэкземпляровканала ограничено только системными ресурсами.

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

ReadFileиWriteFile.

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

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

Г Л А ВА 4 Именованные каналы

85

подразумевает, что именованный канал имеет те же права доступа, что и создавший его процесс (см. модель безопасности Windows NT и Windows 2000 в главе 2). Приложение может предоставить доступ к каналу определенным пользователям и группам, определив структуру SECURITY DESCRIPTOR с помощью API-функций. Чтобы открыть доступ к серверу любым клиентам, в структуре SECURITY^DESCRIPTOR следует задать пустой список DACL.

Когда функция CreateNamedPipe вернет описатель именованного канала, сервер начинает ждать соединения клиентов. Для установления соединения предназначена функция ConnectNamedPipe, которая определена так:

BOOL ConnectNamedPipe(

HANDLE hNamedPipe,

LPOVERLAPPED lpOverlapped

Параметр hNamedPipe — это описатель экземпляра именованного канала, возвращенный функцией CreateNamedPipe. Если при создании канала использовался флаг FILE_FLAG_OVERLAPPED, параметр lpOverlapped позволяет ConnectNamedPipe выполняться асинхронно, то есть без блокировки. Если этот параметр равен NULL, ConnectNamedPipe блокируется, до тех пор пока клиент не установит соединение с сервером.

Вызов функции ConnectNamedPipe завершается после установления соединения. Далее сервер отправляет и принимает данные с помощью APIфункций WriteFile и ReadFile. Когда обмен данными с клиентом завершен, сервер должен закрыть соединение, вызвав функцию DisconnectNamedPipe. В листинге 4-1 показано, как написать простой сервер, способный обмениваться данными с одним клиентом.

Листинг 4-1. Простой сервер именованного канала

// Server.срр

«include <windows.h> «include <stdio.h>

void main(void)

HANDLE PipeHandle;

DWORD BytesRead; CHAR buffer[256];

if ((PipeHandle = CreateNaraedPipe("\\\\-\\Pipe\\Jim", PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1,e 0, 0, 1000, NULL)) == INVALID_HANDLE_VALUE)

printfC'CreateNamedPipe failed with error Xd\n",

GetLastErrorQ);

return;

}

см.спед.стр.

86

ЧАСТЬ I Устаревшие сетевые API

Листинг 4-1. {продолжение) printf( Server is now runninfl\n ),

if (ConnectNamedPipe(PipeHandle, NULL) == 0)

{

printf( ConnectNamedPipe failed with error Xd\n GetLastErrorO),

CloseHandle(PipeHandle),

return,

if (ReadFile(PipeHandle, buffer, sizeof(buffer), &BytesRead, NULL) <= 0)

{

printf( ReadFile failed with error Xd\n , GetLastErrorO), CloseHandle(PipeHandle),

return,

p r i n t f ( % *s\n', BytesRead, buffer),

if (DisconnectNamedPipe(PipeHandle) == 0)

{

p r i n t f ( DisconnectNamedPipe failed with error Xd\n", GetLastErrorO),

return,

}

CloseHandle(PipeHandle),

ФормированиепустогоспискаDACL(NullDACL)

При использовании API-функций для создания таких объектов, как файлы и именованные каналы, Windows NT и Windows 2000 позволяют приложениям задавать права доступа с помощью структуры SECURITY_ATTRIBUTES

typedef struct _SECURITY_ATTRIBUTES {

DWORD

nLength,

LPVOID

lpSecurityDescriptor,

BOOL

blnheritHandle

} SECURITY_ATTRIBUTES,

Поле lpSecurityDescriptor — указатель на структуру SECURITY_ DESCRIPTOR, определяющую права доступа к объекту Структура SECURITY'_DESCRIPTOR содержит поле DACL со списком пользователей и групп, имеющих доступ к объекту Если это поле равно NULI, объект доступен любому пользователю

Г Л А ВА 4 Именованные каналы

87

Приложения не могут напрямую обращаться к структуре SECURITY^ DESCRIPTOR для этого существуют специальные API-функции Напри мер, чтобы создать пустой список DACL в структуре SECURITY_ DESCRIPTOR, необходимо сделать следующее

1 Создайте и инициализируйте структуру SECURITYJDESCRIPTOR с

помощью API-функции InitiahzeSecuntyDescrtptor

2 Присвойте полю DACL значение NUIL с помощью функции SetSecuntyDescnptorDacl

Созданную структуру SECURITYJDESCRIPTOR необходимо поместить в структуру SECURITY_ATTRIBUTES, после чего ее можно использовать при вызове таких API-функций, как CreateNamedPipe

И Создание объектов структур SECURITY.ATTRIBUTES и SECURITY.DESCRIPTOR SECURITY_ATTRIBUTES sa,

SECURITY_DESCRIPTOR sd,

// Инициализация нового объекта SECURITY_DESCRIPTOR для очистки значений if (ImtializeSecuntyDescriptor(&sd, SECURITY_DESCRIPT0R_REVISION)

== 0)

{

printf( InitializeSecurityDescnptor failed with error Xd\n , GetLastErrorO),

return,

// Присвоение полю DACL структуры

SECURITY_DESCRIPTOR

значения NULL

if (SetSecurityDescnptorDacl(&sd

TRUE, NULL, FALSE)

== 0)

{

 

 

printf( SetSecurityDescriptorDacl failed with error Xd\n', GetLastErrorO),

return,

// Назначение нового объекта SECURITY_DESCRIPTOR структуре SECURITY_ATTRIBUTES sa nLength = sizeof(SECURITY_ATTRIBUTES),

sa lpSecurityDescriptor = &sd, sa blnhentHandle = TRUE,

Усовершенствованныйсерверканалов

В листинге 4-1 приведен код сервера, обслуживающего только один экземпляр именованного канала При этом все API-вызовы выполняются синхронно, то есть каждый вызов ждет окончания предыдущего запроса ввода-выво- Да Чтобы позволить двум и более клиентам установить соединение, сервер Должен поддерживать несколько экземпляров именованного канала, максимальное число которых ограничено значением параметра nMaxInstances

Функции CreateNamedPipe

Несколько экземпляров канала можно создать с помощью потоков, либо еханизмов асинхронного ввода-вывода Win32, например перекрытого вво-