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

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


Скачать 2.88 Mb.
НазваниеЭ. Джонс, Д. Оланд
АнкорПрограммирование в сетях Windows.pdf
Дата12.10.2017
Размер2.88 Mb.
Формат файлаpdf
Имя файлаПрограммирование в сетях Windows.pdf
ТипКнига
#9346
страница9 из 50
1   ...   5   6   7   8   9   10   11   12   ...   50
ЧАСТЬ I Устаревшие сетевые API
Табл. 4-1. (продолжение)
Режим
открытия
Флаг
Описание
Управление вводом- выводом
FILE FLAGJWRITE_
THROUGH
FILE FLAG OVERLAPPED
Безопасность WRITE DAC
Функции записи не возвращают значе- ние, пока данные передаются по сети или находятся в буфере удаленного ком- пьютера. Применяется только для име- нованных каналов, работающих в по- байтовом режиме
Позволяет использовать перекрытый ввод-вывод при выполнении операций чтения, записи и соединения
Позволяет приложению изменять список DACL именованного канала
ACCESS SYSTEM ^SECURITY Позволяет приложению изменять список SACL именованного канала
WRITE OWNER
Позволяет приложению изменять владельца именованного канала и групповой SID
Флаги PIPE_ACCESS_ определяют направление передачи данных между сервером и клиентом. Если открыть канал с флагом PIPE ^ACCESS_DUPLEX, пе- редача по нему будет двунаправленной. При открытии канала с флагом Р1РЕ_
ACCESSJNBOUND или PIPE_ACCESS_OUTBOUND — однонаправленной: дан- ные будут передаваться только от клиента серверу или наоборот. На рис. 4-2
изображены комбинации флагов и направление передачи между сервером и клиентом.
PIPE_ACCESSJ)UPLEX
PIPE_ACCESS_OUTBOUND
PIPE_ACCESSJI\IBOUND
Двунаправленная передача
Однонаправленная передача
Однонаправленная передача
GENERIC READI
GENERICIWRITE
GENERIC_READ
GENERIC_WRITE
Сервер Клиент
Рис. 4-2. Флаги режимов и направление передачи
Следующий набор флагов управляет операциями ввода-вывода сервера.
При применении флага EILE_ELAG_WRITE_THROUGH функции записи не воз- вращают значение, пока данные не будут переданы по сети или не появятся в буфере удаленного компьютера. Этот флаг используют, когда сервер и кли- ент находятся на разных компьютерах и именованный канал работает в по-

Г Л А В А 4 Именованные каналы 83
байтовом режиме. Флаг FILEFLAG^OVERLAPPED позволяет функциям, выпол- няющим операции чтения, записи и соединения, немедленно возвращать значение, даже если их выполнение требует существенных затрат времени.
Перекрытый ввод-вывод мы подробно обсудим в разделе, посвященном раз- работке более сложного сервера.
Последняя группа флагов — dwOpenMode (табл. 4-1), управляет доступом сервера к дескриптору безопасности, созданному именованным каналом. Их ьспользуют, если приложению требуется обновить или изменить дескрип- тор после создания канала. Флаг WRITE DAC позволяет приложению обнов- лять список избирательного управления доступом (discretionary access cont- rol 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_READ-
MODEJBYTE | 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 нельзя использовать с флагом PIPEREAD-
MODE_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
«include
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, b u f f e r ) ,
if (DisconnectNamedPipe(PipeHandle) == 0)
{
p r i n t f ( DisconnectNamedPipe f a i l e d with error Xd\n",
GetLastErrorO),
return,
}
CloseHandle(PipeHandle),
Формирование пустого списка DACL (Null DACL)
При использовании API-функций для создания таких объектов, как файлы и именованные каналы, Windows NT и Windows 2000 позволя- ют приложениям задавать права доступа с помощью структуры SE-
CURITY _ATTRIBUTES
typedef struct _SECURITY_ATTRIBUTES {
DWORD nLength,
LPVOID lpSecurityDescriptor,
BOOL blnheritHandle
} SECURITY_ATTRIBUTES,
Поле lpSecurityDescriptor — указатель на структуру SECURITY_ DESC-
RIPTOR, определяющую права доступа к объекту Структура SECURI-
TY'_DESCRIPTOR содержит поле DACL со списком пользователей и групп, имеющих доступ к объекту Если это поле равно NULI, объект доступен любому пользователю

Г Л А В А 4 Именованные каналы
87
Приложения не могут напрямую обращаться к структуре SECURITY^
DESCRIPTOR для этого существуют специальные API-функции Напри мер, чтобы создать пустой список DACL в структуре SECURITY_ DE-
SCRIPTOR, необходимо сделать следующее
1 Создайте и инициализируйте структуру SECURITYJDESCRIPTOR с помощью API-функции InitiahzeSecuntyDescrtptor
2 Присвойте полю DACL значение NUIL с помощью функции Set-
SecuntyDescnptorDacl
Созданную структуру 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, например перекрытого вво-

88
ЧАСТЬ I Устаревшие сетевые API
да-вывода и портов завершения. Механизмы асинхронного ввода-вывода позволяют одновременно обслуживать все экземпляры канала из одного потока приложения. Рассмотрим, как создать более сложный сервер с помо- щью потоков и перекрытого ввода вывода (подробнее о портах заверше- ния — в главе 8).
Потоки
Разработать сложный сервер, способный поддерживать более одного соеди- нения с помощью потоков, не так уж трудно. Для каждого соединения необ- ходимо создать отдельный поток и работать с ним, как с простым сервером.
В листинге 4-2 показан код сервера, обслуживающего пять экземпляров име- нованного канала. Это приложение — эхо-сервер, получающий данные от клиента и отправляющий их обратно.
Листинг 4-2. Использование потоков при реализации сложного сервера
именованного канала
// Threads.срр
«include
«include
«include
«define NUM_PIPES 5
DWORD WINAPI PipeInstanceProc(LPVOID lpParameter);
void main(void)
HANDLE ThreadHandle;
INT i;
DWORD Threadld;
for(i = 0; i < NUH_PIPES;

// Создание потока для обслуживания каждого экземпляра именованного канала
if ((ThreadHandle = CreateThread(NULL, 0, PipelnstanceProc,
NULL, 0, &ThreadId)) == NULL)
{
printfC'CreateThread failed with error X\n",
GetLastErrorO);
return;
}
CloseHandle(ThreadHandle);
p r i n t f ( " P r e s s a key to stop the server\n");
_getch();
OO ОНЯ

Г Л А В А 4 Именованные каналы 89
Листинг 4-2. (продолжение)
II функция: PipelnstanceProc
// описание:
// Эта функция обрабатывает один экземпляр именованного канала
DWORD WINAPI PipeInstanceProc(LPVOID lpParameter)
HANDLE PipeHandle;
DWORD BytesRead;
DWORD BytesWritten;
CHAR Buffer[256];
if ((PipeHandle = CreateNamedPipe("\\\\A\PIPE\\jim",
PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
NUM.PIPES, 0, 0, 1000, NULL)) == INVALID_HANDLE_VALUE)
printfC'CreateNamedPipe failed with error Xd\n",
GetLastErrorO);
return 0;
// Обслуживание соединений клиентов в бесконечном цикле
whiled)
{
if (ConnectNamedPipe(PipeHandle, NULL) == 0)
{
printf("ConnectNamedPipe failed with error Xd\n",
GetLastErrorO);
«• break;
•JH
II Чтение данных и отправка их обратно клиенту до тех пор,
// пока он не прекратит передачу
whlle(ReadFile(PipeHandle, Buffer, sizeof(Buffer),
&BytesRead, NULL) > 0)
{
pnntf("Echo Xd bytes to client\n", BytesRead); <.u
*• if (WriteFile(PipeHandle, Buffer, BytesRead,
&BytesWritten, NULL) == 0) 3
printf("WriteFile failed with error Xd\n",
1
GetLastErrorO);
break;
}
}
;[3XTS см. след. стр.

90
ЧАСТЬ I Устаревшие сетевые API
Листинг 4-2. {продолжение)
if (DisconnectNamedPipe(PipeHandle) == 0)
{
printfC'DisconnectNamedPipe failed with error Xd\n",
GetLastErrorO),
break;
CloseHandle(PipeHandle);
return 0;
С помощью API-функции CreateThread сервер запускает пять потоков,
каждый из которых выполняет функцию PipelnstanceProc Функция Pipelns-
tanceProc работает точно так же, как простой сервер (листинг 4-1) Сеанс связи завершается вызовом функции DisconnectNamedPipe, после чего при- ложение снова вызывает функцию ConnectNamedPtpe с тем же описателем,
чтобы обслужить другого клиента
Перекрытый ввод-вывод
Это механизм асинхронного выполнения таких API-функций, как ReadFile и
WnteFile Прежде всего в функцию необходимо передать структуру OVERLAPPED,
которая впоследствии будет использована для получения результатов запроса ввода-вывода с помощью API-функции GetOverlappedResult Если функция вы- зывается с OVERLAPPED, результат возвращается немедленно
Чтобы сервер обслуживал несколько экземпляров именованного канала с помощью перекрытого ввода-вывода, значение параметра nMaxInstances
функции CreateNamedPipe должно быть больше 1, а параметр dwOpenMode
равен FILE_FLAG_OVERLAPPED На листинге 4-3 показан код сервера, обслу- живающего пять экземпляров именованного канала Это приложение — эхо- сервер, который получает данные от клиента и отправляет их обратно
Листинг 4-3. Использование перекрытого ввода-вывода при реализации усовершенствованного сервера именованных каналов
// Overlap cpp
1   ...   5   6   7   8   9   10   11   12   ...   50


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