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

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


Скачать 2.88 Mb.
НазваниеЭ. Джонс, Д. Оланд
АнкорПрограммирование в сетях Windows.pdf
Дата12.10.2017
Размер2.88 Mb.
Формат файлаpdf
Имя файлаПрограммирование в сетях Windows.pdf
ТипКнига
#9346
страница10 из 50
1   ...   6   7   8   9   10   11   12   13   ...   50
«include
«include
«define NUM_PIPES 5
«define BUFFER SIZE 256
(C
void mam(void)
{ if i'ii,i
HANDLE PipeHandles[NUM_PIPES];
DWORD BytesTransferred,
\ CHAR Buffer[NUM_PIPES][BUFFER_SIZE];
jefl

Г Л А В А 4 Именованные каналы 91
Листинг 4-3. (продолжение)
INT 1,
OVERLAPPED Ovlap[NUM_PIPES]; •
HANDLE Event[NUM_PIPES],
// Сервер должен хранить текущее состояние каждого канала Для этого
// предназначен массив DataRead Исходя из текущего состояния канала,
// сервер будет определять, какую операцию ввода-вывода необходимо выполнить.
BOOL DataRead[NUM_PIPES],
DWORD Ret;
DWORD Pipe;
for(i = 0, l < NUM_PIPES, i++)
{
// Создание экземпляра именованного канала if ((PipeHandles[i] = CreateNamedPipe("\\\\.\\PIPE\\jiin",
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, NUM_PIPES,
0, 0, 1000, NULL)) == INVALID_HANDLE_VALUE)
{
pnntfC'CreateNamedPipe for pipe Xd failed "
"with error Xd\n", l, GetLastErrorO),
return,
// Для каждого экземпляра канала создается обработчик события,
// который будет использоваться для мониторинга активности
// операций перекрытого ввода-вывода if ((Eventfi] = CreateEvent(NULL, TRUE, FALSE, NULL))
== NULL)
{
printf("CreateEvent for pipe Xd failed with error Xd\n",
l, GetLastErrorO);
continue,
// Инициализация флага состояния, определяющего следует ли записывать
// и читать из канала
DataRead[i] = FALSE;
ZeroMemory(&Ovlap[l], sizeof(OVERLAPPED));
0vlap[i].hEvent = Event[i];
// Прослушивание клиентских соединений с помощью функции ConnectNamedPipeO
if (ConnectNamedPipe(PipeHandles[i], &0vlap[i]) == 0)
{
if (GetLastErrorO != ERROR_IO PENDING)
{
"••iw ш.след.стр.

92 ЧАСТЬ I Устаревшие сетевые API
Листинг 4-3. (продолжение)
pnntfCConnectNamedPipe for pipe Xd failed with"
" error Xd\n", l, GetLastErrorO);
CloseHandle(PipeHandles[i]);
return,
}
}
}
printf ("Server is now runmng\n");
// Чтение и отправка данных обратно клиенту в бесконечном цикле
while(1)
{
if ((Ret = WaitForMultipleObjects(NUM_PIPES, Event,
FALSE, INFINITE)) == WAIT_FAILED)
{
printf("WaitForMultipleObjects failed with error Xd\n",
GetLastErrorO);
return;
Pipe = Ret - WAIT_0BJECT_0,
ResetEvent(Event[Pipe]);
// Проверка результатов. При возникновении ошибки соединение
// устанавливается заново, иначе выполняется чтение и запись
if (GetOverlappedResult(PipeHandles[Pipe], &Ovlap[Pipe],
&BytesTransferred, TRUE) == 0)
{
printf("GetOverlapped result failed Xd start over\n",
GetLastErrorO);
if (DisconnectNamedPipe(PipeHandles[Pipe]) == 0)
< Л
printf("DisconnectNamedPipe failed with error Xd\nV н \\
v
GetLastErrorO); *4sJe0
return;
} AJDW «XoieS
* ']
n
if (ConnectNamedPipe(PipeHandles[Pipe],
<№ &0vlap[Pipe]) == 0) ix вин*, jqfi \\
, { ХЧЬвадИ i>0) tt
if (GetLastErrorO 1= ERROR_IO_PENDING) }
{
// Обработка ошибки канала и закрытие описателя
If- printf("ConnectNamedPipe for pipe Jfd failed with"

Г Л А В А 4 Именованные каналы
,
9 3
Листинг 4-3. (продолжение)
" error Xd\n", i, GetLastError()),
CloseHandle(PipeHandles[Pipe]),
DataRead[Pipe] = FALSE,
}
else
{
// Проверка состояния канала. Если значение переменной
// DataRead равно FALSE, асинхронно читаем отправленные клиентом данные,
// иначе отправляем данные обратно клиенту.
if (DataRead[Pipe] == FALSE)
// Подготовка к чтению данных от клиента путем
// асинхронного вызова функции ReadFile
ZeroMemory(&Ovlap[Pipe], sizeof(OVERLAPPEO));
Ovlap[Pipe].hEvent = Event[Pipe];
if (ReadFile(PipeHandles[Pipe], Buffer[Pipe],
BUFFER.SIZE, NULL, &Ovlap[Pipe]) == 0)
if (GetLastErrorO i= ERROR_IO_PENDING)
printfC'ReadFile failed with error Xd\n",
GetLastErrorO);
DataRead[Pipe] = TRUE;
}
else
{
// Отправка данных обратно клиенту путем
// асинхронного вызова функции WriteFile
printf("Received Xd bytes, echo bytes back\n",
BytesTransferred);
2eroMemory(&0vlap[Pipe], sizeof(OVERLAPPED));
Ovlap[Pipe].hEvent = Event[Pipe];
if (WnteFile(PipeHandles[Pipe], Buffer[Pipe],
BytesTransferred, NULL, &0vlap[Pipe]) == 0)
{
if (GetLastErrorO != ERROR_IO_PENDING)
см. след. стр.

94
ЧАСТЬ I Устаревшие сетевые API
Листинг 4-3. (продолжение)
pnntf( WriteFile failed with error Xd\n
GetLastErrorO),
DataRead[Pipe] = FALSE,
} 1ЭЗ fl.'UH«>
Чтобы получить описатели каждого канала, приложение пять раз вызывает функцию CreateNamedPipe Затем сервер начинает прослушивать каждый канал
Для этого он пять раз асинхронно вызывает функцию ConnectNamedPipe, пере- давая ей структуру OVERLAPPED При подключении клиента все операции ввода- вывода обрабатываются асинхронно По завершении соединения с клиентами сервер вызывает функцию DisconnectNamedPipe, а затем повторно инициирует прослушивание каждого канала с помощью функции ConnectNamedPipe
Олицетворение
Именованные каналы позволяют воспользоваться встроенными возможно- стями защиты Windows NT и Windows 2000 для управления доступом кли- ентов Windows NT и Windows 2000 поддерживают так называемое олицет- ворение, позволяющее серверу выполняться в контексте безопасности кли- ента Обычно сервер именованного канала работает в контексте безопасно- сти процесса, который его запустил Например, если сервер именованного канала запущен пользователем, обладающим привилегиями администрато- ра, этот пользователь получает доступ практически к любым ресурсам Win- dows NT или Windows 2000 Данная ситуация не безопасна, ведь структура
SECURITY_ DESCRIPTOR, переданная в функцию CreateNamedPipe, открывает доступ к каналу любым пользователям
Установив соединение с клиентом с помощью функции ConnectNamedPipe,
сервер может заставить свой поток выполняться в контексте безопасности кли- ента, вызвав API-функцию ImpersonateNamedPipeChent
BOOL ImpersonateNamedPipeClient(
HANDLE hNamedPipe
Параметр hNamedPipe — это описатель экземпляра именованного кана- ла, возвращенный функцией CreateNamedPipe При вызове функции Imper-
sonateNamedPipeChent операционная система меняет контекст безопаснос- ти сервера на контекст безопасности клиента В результате при обращении к разным ресурсам (например, файлам) сервер будет использовать права до- ступа клиента Это позволяет сохранить управление доступом к ресурсам, не- зависимо от того, кто запустил серверное приложение
При работе в контексте безопасности клиента поток сервера использует один из четырех основных уровней олицетворения анонимный (Апопу-

Г Л А В А 4 Именованные каналы > 95
mous), идентификация (Identification), олицетворение (Impersonation) и де- легирование (Delegation) Уровни олицетворения определяют степень, до которой сервер вправе представлять клиента Завершив сеанс связи, сервер должен вызвать функцию RevertToSelf, чтобы вернуться к первоначальному контексту безопасности Эта функция не имеет параметров
BOOL RevertToSelf(VOID),
Детали реализации клиента
Реализация клиента именованного канала подразумевает разработку прило- жения, которое может подключаться к серверу Клиенты не могут создавать экземпляры именованных каналов и устанавливают соединение с уже суще- ствующими на сервере Для создания простого клиента выполните следую- щие действия
1 Для проверки наличия свободного экземпляра канала вызовите API-функ- цию WaitNamedPipe
2 Для установления соединения используйте API-функцию CreateFile
3 Для отправки и получения данных используйте API-функции WrtteFtle и
ReadFile
4 Для завершения соединения используйте API-функцию CloseHandle
Перед установлением соединения клиент должен проверить наличие сво- бодного экземпляра именованного канала функцией WaitNamedPipe
BOOL WaitNamedPipe(
LPCTSTR lpNamedPipeName,
DWORD nTimeOut
),
Параметр lpNamedPipeName определяет название именованного канала в формате UNC, а параметр nTimeOut — сколько времени клиент будет ждать свободного экземпляра канала
В случае успешного выполнения функции WaitNamedPipe откройте экзем- пляр именованного канала с помощью API-функции CreateFile
HANDLE CreateFile(
LPCTSTR lpFlleName,
DWORD dwDesiredAccess,
DWORD dwShareMode,
LPSECURITY.ATTRIBUTES lpSecurltyAttributes,
DWORD dwCreatlonDlsposition,
DWORD dwFlagsAndAttrlbutes,
HANDLE hTemplateFile
Параметр lpFlleName — название открываемого канала в формате UNC
араметр dwDesiredAccess задает режим доступа и должен быть равен GENE-
MCjtEAD при чтении, или GENERIC_WRTTE — при записи данных в канал
Можно указать оба флага, объединив их с помощью операции OR Режим до-

96
ЧАСТЬ I Устаревшие сетевые API
ступа должен соответствовать направлению передачи (параметр
dwOpenMode), заданному при создании канала Например, если канал создан с флагом PLPE_ACCESSJNBOUND, клиент указывает флаг GENERICJWR1TE
Параметр dwShareMode должен быть равен 0, поскольку в каждый момент времени только один клиент может получить доступ к экземпляру канала
Параметр ipSecuntyAttnbutes NULL, если вы не хотите, чтобы дочерний процесс наследовал описатель клиента Этот параметр нельзя использовать для управления доступом, так как с помощью функции CreateFtle невозмож- но создать экземпляры именованного канала Параметр dwCreationDisposi-
tion следует определить как OPEN_EXISTING, тогда функция CreateFile будет возвращать ошибку, если канала не существует
Параметр dwFlagsAndAttnbutes обязательно должен включать флаг FILE_
ATTRIBUTE_NORMAL Кроме того, можно указать флаги FILE_FLAG_
WRITEJTHROUGH, FILEJ'LAG^OVERLAPPED и SECURITYJQOS PRESENT, объе- динив их с перрым операцией ИЛИ Флаги FILE_FLAG_WR1TE_ THROUGH и
FILE_FLAGjOVERLAPPED предназначены для управления вводом-выводом (см.
описание функции CreateNamedPipe) Флаг SECURITY_SQOS_ PRESENT опре- деляет уровень олицетворения клиента на сервере именованного канала
Уровни олицетворения задают степень свободы действий серверного про- цесса от имени клиентского Клиент указывает эту информацию при под- ключении к серверу Если клиент выбирает флаг SECURITY_SQOS_ PRESENT,
он должен указать один или несколько флагов, перечисленных в следующем списке
Ш SECURITYANONYMOUS — задает уровень анонимности Сервер не может получить информацию о клиенте и выполняться в контексте безопасно- сти клиента
SECURITYIDENTIFICATION — задает уровень идентификации Сервер может получить информацию о клиенте, например идентификаторы и привилегии защиты, но не выполняться в контексте безопасности клиен- та Это полезно, когда серверу необходимо идентифицировать клиента,
но не нужно играть его роль
И SECURITYIMPERSONATION задает уровень олицетворения Сервер может получить информацию о клиенте, а контекст безопасности клиен- та распространяется только на локальную систему Этот флаг позволяет серверу получить доступ к любым локальным ресурсам на сервере, как если бы он был клиентом Сервер не может представлять клиента на уда- ленных системах
SECURITYDELEGATION — задает уровень делегирования Сервер может получить информацию о клиенте и выполняться в контексте безопасно- сти клиента на его локальной системе и удаленных системах
ПРИМЕЧАНИЕ В Windows NT не реализовано делегирование безопас- ности, поэтому флаг SECURITYJDELEGATION следует применять, толь- ко если сервер работает под управлением Windows 2000
01

Г Л А В А 4 Именованные каналы < 97
т SECUR1TYCONTEXTTRACKING — задает динамический режим наблю- дения защиты Если этот флаг не указан, то режим статический
Ш SECURITYEFFECTIVEONLY указывает, что только включенные ас- пекты контекста безопасности клиента доступны серверу Если этот флаг не назначен, серверу доступны любые аспекты контекста безопас- ности клиента
Последний параметр функции CreateFile hTemplateFtle, не применяет- ся при работе с именованными каналами и должен быть равен NULL После успешного выполнения функции CreateFile, клиент может отправлять и при- нимать данные с помощью функций ReadFile и WnteFile Завершив переда- чу, клиент закрывает соединение функцией CloseHandle
В листинге 4-4 приведен код простого клиента именованного канала При успешном соединении клиент отправляет серверу сообщение «This is a test».
Листинг 4-4. Простой клиент именованного канала
// Client cpp
«include
((include
((define PIPE.NAME
M
\\\\.\\Pipe\\jinr void mam(void)
{
HANDLE PipeHandle,
DWORD BytesWntten;
if (WaitNamedPipe(PIPE_NAME, NMPWAIT_WAIT_FOREVER) == 0)
{
printfC'WaitNamedPipe failed with error SSd\n',
GetLastError()),
return,
// Создание описателя файла именованного канала if ((PipeHandle = CreateFile(PIPE_NAME,
GENERIC_READ | GENERIC_WRITE, 0,
(INSECURITY,ATTRIBUTES) NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
(HANDLE) NULL)) == INVALID HANDLE_VALUE)
{
printf("CreateFile failed with error Xd\n", GetLastErrorO);
return,
if (WriteFile(PipeHandle, 'This is a test', 14, &BytesWritten,
NULL) == 0)
см. след. стр.

98 ЧАСТЬ I Устаревшие сетевые API
Листинг 4-4. (продолжение)
{
printf( WriteFile failed with error Xd\n , GetLastErrorQ),
CloseHandle(PipeHandle),
return,
p n n t f ( Wrote Xd bytes , BytesWritten);
CloseHandle(PipeHandle),
Другие API-вызовы
Теперь обсудим ряд дополнительных функций, облегчающих работу с име- нованными каналами Прежде всего, рассмотрим функции CallNamedPipe и
TransactNamedPipe, которые могут существенно упростить код приложения
Обе функции выполняют операции чтения и записи в одном вызове Функция
CallNamedPipe позволяет клиенту подключиться к именованному каналу ра- ботающему в режиме сообщений (и подождать, пока не освободится экземп- ляр канала), записать и считать данные, а затем закрыть канал Фактически,
она является полноценным клиентом именованного канала
BOOL CallNamedPipe(
LPCTSTR lpHamedPipeName,
LPVOID lpInBuffer,
DWORD nlnBufferSize,
LPVOID IpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesRead,
DWORD nTimeOut
),
Параметр ipNamedPipeName определяет название именованного канала в формате UNC Параметры lpInBuffer и nlnBufferSize — адрес и размер буфера,
используемого приложением для записи данных Аналогично, IpOutBuffer и
nOutBufferSize определяют адрес и размер буфера чтения Параметр lpBytes-
Read содержит количество байтов, считанных из канала Параметр nTimeOut
определяет время ожидания освобождения канала в миллисекундах
Функция TransactNamedPipe используется как в клиентских, так и в сер- верных приложениях Она объединяет операции чтения и записи в одном
API-вызове Это оптимизирует сетевой трафик за счет сокращения числа транзакций, выполненных через перенаправитель MSNP Функция Transact-
NamedPipe определена так
BOOL TransactNamedPipe(
HANDLE hNamedPtpe,
LPVOID lpInBuffer,
DWORD nlnBufferSize,
LPVOID IpOutBuffer,

Г Л А В А 4 Именованные каналы '99
DWORD nOutBufferSize,
LPDWORD lpBytesRead,
LPOVERLAPPED lpOverlapped
),
Параметр hNamedPipe — это описатель именованного канала, возвра- щенный функцией CreateNamedPipe или CreateFile Параметры iplnBuffer и
nlnBufferSize определяют адрес и размер буфера, используемого приложе- нием для записи данных Аналогично ipOutBuffer и nOutBufferSize обозна- чают адрес и размер буфера чтения Параметр lpBytesRead содержит ко- личество байтов, считанных из канала Параметр lpOverlapped позволяет функции TransactNamedPipe выполняться асинхронно путем перекрытого ввода-вывода
Следующая группа функций GetNamedPipeHandleState, SetNamedPipe-
HandleState и GetNamedPipelnfo, — обеспечивает более гибкое взаимодей- ствие сервера и клиента во время выполнения Например, с помощью этих функций можно сменить режим работы именованного канала с побайтово- го на режим сообщений Функция GetNamedPipeHandleState возвращает ин- формацию о канале, включая режим работы, количество экземпляров кана- ла и информацию о состоянии буферов Информация, возвращаемая этой функцией, может изменяться в процессе работы именованного канала
Функция определена так
BOOL GetNamedPipeHandleState(
HANDLE hNamedPipe,
LPDWORD lpState,
LPDWORD lpCurlnstances,
LPDWORD lpMaxCollectionCount,
LPDWORD lpCollectDataTimeout,
LPTSTR lpUserName,
DWORD nMaxUserNameSize
),
Параметр hNamedPipe — это описатель именованного канала, возвращен- ный функцией CreateNamedPipe или CreateFile Параметр lpState содержит текущий режим работы канала и принимает значения PIPE_NOWAIT или
PIPE_READMODE_MESSAGE Параметр lpCurlnstances содержит текущее число экземпляров канала, lpMaxCollectionCount — максимальное число байтов,
которые будут накоплены на компьютере клиента перед передачей на сер- вер, lpCollectDataTimeout — максимальное время в миллисекундах, которое может пройти, до того как удаленный клиент передаст информацию по сети
Параметры lpUserName и nMaxUserNameSize определяют буфер, содержащий заканчивающуюся 0 строку с именем пользователя клиентского приложения
Функция SetNamedPipeHandleState позволяет изменить характеристики канала, возвращенные функцией GetNamedPipeHandleState
B 0 0 L Se
™amedPipeHandleState(
HANDLE hNamedPipe
LPDWORD lpMode,

100
ЧАСТЬ I Устаревшие сетевые API
1   ...   6   7   8   9   10   11   12   13   ...   50


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