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

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

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

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 определяют адрес и размер буфера чтения Параметр lpBytesRead содержит количество байтов, считанных из канала Параметр nTimeOut определяет время ожидания освобождения канала в миллисекундах

Функция TransactNamedPipe используется как в клиентских, так и в серверных приложениях Она объединяет операции чтения и записи в одном API-вызове Это оптимизирует сетевой трафик за счет сокращения числа транзакций, выполненных через перенаправитель MSNP Функция TransactNamedPipe определена так

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, SetNamedPipeHandleState и 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(

HANDLEhNamedPipe

LPDWORDlpMode,

100

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

LPDWORD lpHaxCollectionCount,

LPDWORD lpCollectDataTimeout

Параметр hNamedPipe — это описатель именованного канала, возвращенный функцией CreateNamedPipe или CreateFile. Параметр ipMode задает режим работы именованного канала. Параметр IpMaxCollectionCount содержит максимальное число байтов, которые будут накоплены на компьютере клиента перед передачей на сервер. Параметр lpCollectDataTimeout определяет максимальное время в миллисекундах, которое может пройти, до того как удаленный клиент передаст информацию по сети.

Функция GetNamedPipelnfo возвращает размер буферов и максимальное количество экземпляров канала:

BOOL GetNamedPipeInfo(

HANDLE hNamedPipe,

LPDWORD lpFlags,

LPDWORD IpOutBufferSize,

LPDWORD IpInBufferSize,

LPDWORD lpMaxInstances

Параметр hNamedPipe — это описатель именованного канала, возвращенный функцией CreateNamedPipe или CreateFile. Параметр lpFlags указывает вид и режим работы именованного канала и определяет, является ли он сервером или клиентом. Параметры IpOutBufferSize и IpInBufferSize содержат размер исходящего и входящего внутренних буферов, ipMaxInstance — максимальное количество экземпляров канала.

Последняя функция — PeekNamedPipe, позволяет просмотреть находящиеся в канале данные, не удаляя их из внутреннего буфера. Например, проверить наличие входящих данных и избежать блокировки функции ReadFile. Кроме того, эта функция полезна, если необходимо проверить данные перед получением: приложение может скорректировать размер своего буфера в зависимости от размера входящего сообщения. Функция PeekNamedPipe on- j ределена так:

BOOL PeekNamedPipe(

HANDLE hNamedPipe,

LPVOID lpBuffer,

DWORD nBufferSize,

LPDWORD lpBytesRead,

LPDWORD lpTotalBytesAvail,

LPDWORD lpBytesLeftThlsHessage

Параметр hNamedPipe — это описатель именованного канала, возвращен-» ный функцией CreateNamedPipe или CreateFile. Параметры lpBuffer и nBufferSize определяют адрес и размер принимающего буфера. Параметр 1рBytesRead содержит количество байтов, считанных из канала в буфер lpBuffer-, lpTotalBytesAvail — общее количество байтов, которое можно считать из ка-

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

101

нала; ipBytesLeftTbisMessage — оставшееся количество байт в сообщении, если канал работает в режиме сообщений. Если сообщение не помещается в буфер ipBuffer, данный параметр содержит оставшееся количество байт. Если именованный канал работает в побайтовом режиме, параметр всегда содержит 0.

Платформа и производительность

В базе знаний Microsoft, к которой можно обратиться по адресу http://support.microsoft.com/support/search, описаны следующие проблемы и ограничения.

Н Q100291 — ограничения на названия именованных каналов. Канал \\.\Pipe\Mypipes\Pipel невозможно создать при наличии канала \\.\Pipe\Mypipes. Название существующего канала нельзя использовать в качестве пути к другому каналу.

И Q119218 — в именованный канал можно записать только 64 кб данных. Если именованный канал работает в режиме сообщений, то при попытке записать данные, используя буфер большего размера, функция WriteFile вернет значение FALSE, а функция GetLastError — ошибку

ERRORMOREDATA.

Ш Q110148 — функции WriteFile или ReadFile возвращают ошибку

ERROR_ INVALID PARAMETER, если при работе с именованным каналом используется перекрытый ввод-вывод. Возможная причина такой ошибки — поля Offset и OffsetHigh структуры OVERLAPPED не равны 0.

Q180222 — функция WaitNamedPipe и ошибка с кодом 253 в Windows 95- Если в Windows 95 в качестве первого параметра функции WaitNamedPipe передать неверное название канала, то функция GetLastError вернет ошибку с кодом 253, которой нет в списке возможных ошибок данной функции. Если то же самое проделать в Windows NT 4, код ошибки изменится на 1б1 (ERROR_BAD PATHNAME). Обрабатывайте ошибку 253 также, как 161.

Q141709 — максимум 49 соединений с одной рабочей станции.

Если сервер именованных каналов создает 49 каналов, клиент на удаленном компьютере не может соединиться со следующим (50-м и далее) экземпляром именованного канала на этом сервере.

Q126645 — ошибка Access Denied (доступ запрещен) при открытии именованного канала из службы. Если служба, запущенная под учетной записью Local System, попытается открыть именованный канал на компьютере с Windows NT, будет выдана ошибка Access Denied (с кодом 5).

Резюме

ои главе мы рассмотрели технологию именованных каналов, которые Доставляют простую архитектуру клиент-сервер для надежной передачи

102

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

данных. Для передачи данных по сети данный интерфейс использует перенаправитель Windows. Основное преимущество именованных каналов — они позволяют воспользоваться встроенными возможностями защиты Windows NT и Windows 2000.

Эта глава завершает первую часть книги, в которой мы обсудили вопросы передачи данных средствами перенаправителя Windows. Во второй части мы рассмотрим технологию Winsock, которая позволяет обмениваться данными напрямую по транспортному протоколу.

Ч А С Т Ь

I

ИНТЕРФЕЙС

ПРИКЛАДНОГО

ПРОГРАММИРОВАНИЯ WINSOCK

Вторая часть книги посвящена программированию средствами Winsock на платформах Win32. Winsock — это сетевой интерфейс прикладного программирования, а не протокол, основной интерфейс доступа к разным базовым сетевым протоколам, реализованный на всех платформах Win32. Интерфейс Winsock унаследовал многое от реализации Berkeley (BSD) Sockets на платформах UNIX, работающих с множеством сетевых протоколов. В средах Win32 он стал абсолютно независимым от протокола, особенно с выпуском версии Winsock 2.

Вследующих трех главах описаны основные характеристики протоколов

иинтерфейса Winsock, включая адресацию для каждого протокола и пример простого клиента и сервера Winsock. Мы рассмотрим новые технологические возможности интерфейса Winsock 2: поставщики службы транспорта, поставщики пространства имен и качество обслуживания (Quality of Service, V°S). В описании этих технологий есть некое несоответствие: хотя они и включены в спецификацию Winsock 2 и этот интерфейс поддерживается на всех современных платформах Win32 (за исключением ОС Windows СЕ), не

се указанные в документации возможности реализованы на каждой из платформ. Об этих ограничениях мы упомянем дополнительно. Для изучения той части книги вы должны обладать базовыми знаниями о Winsock (или

етах BSD) и знать клиент-серверную терминологию Winsock.

Г Л А В А

Сетевые протоколы

Основная цель разработки спецификации Winsock 2 — создать независимый от протокола транспортный интерфейс. К его неоспоримым преимуществам относится предоставление единого привычного интерфейса сетевого программирования для различных транспортов сети. Впрочем, знать характеристики сетевых протоколов вам все же не помешает. В этой главе описаны различные аспекты работы с конкретными протоколами, а также некоторые основные сетевые правила. Кроме того, мы расскажем, как программно получить у Winsock сведения о протоколе, опишем основные шаги создания сокета для конкретных протоколов.

Характеристикипротоколов

В этом разделе мы рассмотрим основные характеристики распространенных транспортных протоколов, а также то, как протокол функционирует в приложении.

Протокол, ориентированный на передачу сообщений

Протокол называют ориентированным на передачу сообщений, если для каждой команды записи он передает байты по сети в отдельном сообщении. Это также означает, что приемник получит данные в виде отдельного сообщения отправителя. Таким образом, приемник получит только одно сообщение. Например, на рис. 5-1 рабочая станция слева отправляет сообщения по 128,64 и 32 байта рабочей станции справа. Принимающая рабочая станция дает три команды чтения с 256-байтным буфером. На каждый запрос последовательно возвращается 128, 64 и 32 байта. Первый запрос чтения не возвращает сразу три пакета, даже если все они уже получены. Таким образом, сохраняются границы сообщений, что часто необходимо для обмена структурированными данными. Например, в сетевой игре каждый участник отправляет другим игрокам пакет данных, с информацией о своей позиции. Программа, лежащая в основе такого обмена данными, очень проста: игрок запрашивает пакет данных и получает от другого участника игры именно один пакет данных с информацией о позиции другого игрока.

Протокол, не сохраняющий границы сообщений, обычно называют протоколом, основанным на потоке. Учтите, что термин «основанный на потоке» (stream-based) часто употребляют некорректно, подразумевая дополнительные характеристики. Потоковая служба непрерывно передает данные:

Г Л А ВА 5 Сетевые протоколы

105

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

Сетевой стек

NetworkStack

 

 

 

 

 

 

 

32 байта

 

128 байт

|

 

ПлатформаWin32

 

t

 

ПлатформаWin32

64байта

 

64 байта |

 

 

 

t

 

 

128 байт

 

32 байта |

 

 

 

 

 

 

 

Сеть

Рис. 5-1. Службы дейтаграмм

На рис. 5-2 отправитель передает пакеты данных по 128, 64 и 32 байт, однако стек локальной системы может принимать данные более крупными пакетами. В данном случае последние два пакета передаются вместе.

Сетевой стек

Сетевой стек

 

32байта

 

 

ПлатформаWin32

224 байта

ПлатформаWin32

64 байта

 

 

128 байт

 

 

Сеть

Рис. 5-2. П о т о к о в ы е с л у ж б ы

Решение объединить дискретные пакеты данных зависит от нескольких факторов, например, от максимального размера блока передаваемой информации или применения алгоритма Nagle. В отношении TCP/IP применение agle заключается в том, что узел накапливает данные перед отправкой: ждет, пока накопится достаточно данных или истечет указанный тайм-аут. Парт- еР этого узла, перед тем как отправить узлу уведомление, ожидает исходящие данные в течение указанного времени. Это нужно, чтобы партнеру не

1 06

ЧАСТЬ II Интерфейс прикладного программирования Winsock

пришлось пересылать пакет данных «порожняком» — только с одним уведомлением. Отправка большого количества небольших по размеру пакетов данных неэффективна, поскольку влечет существенные издержки из-за многочисленных проверок на наличие ошибок и обмен подтверждениями. Со стороны получателя сетевой стек накапливает поступающие данные для конкретного процесса. Если получатель считывает данные, обладая 256-байт- ным буфером, то все 224 байта возвращаются сразу. Если приемник требует считать только 20 байт, система вернет только 20 байт.

Псевдопоток

Система с ориентированным на передачу сообщений протоколом отправляет данные дискретными пакетами, которые получатель считывает и буферизует в пул так, чтобы получающее приложение считывало порции данных любого размера. Такую схему обмена данными зачастую и называют псевдопотоком. Понять работу псевдопотока можно, скомбинировав отправителя на рис. 5-1 с получателем на рис. 5-2. Отправитель должен посылать каждый пакет данных по отдельности, но получатель может принимать их как угодно. В основном, перемещение данных псевдопотоком можно рассматривать как обычный, основанный на потоке, протокол.

Обмен данными, с соединением и без него

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

Впрочем, установление канала связи между двумя участниками влечет дополнительные издержки. Кроме того, большинство протоколов, ориентированных на передачу сообщений, гарантируют доставку данных, что еще больше увеличивает издержки, вызванные дополнительными вычислениями для проверки правильности передачи данных. С другой стороны, протокол, не ориентированный на передачу данных, не гарантирует, что приемник фактически принимает данные. Службы, не ориентированные на соединение, схожи с почтовой связью: отправитель адресует письмо определенному человеку и опускает его в почтовый ящик. Однако он не знает, существует ли вообще получатель письма, или не помешает ли сильная буря почтовой службе доставить послание.

Надежность и порядок доставки сообщений

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

Г Л А ВА 5 Сетевые протоколы

Т07

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

Также необходимо принять во внимание порядок, в котором данные поступают получателю. Протокол, сохраняющий порядок данных, гарантирует что приемник получит эти данные в том порядке, в котором они были отправлены. Соответственно, протокол, не сохраняющий порядок байтов, не дает такой гарантии.

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

Заметьте, что сохранение порядка пакетов не гарантирует автоматически целостность данных. Конечно, основное преимущество протоколов, не ориентированных на соединение, — это скорость: они не «заботятся» об установлении виртуального соединения с приемником. Зачем замедлять передачу данных проверкой на ошибки?

В общем, протоколы, не ориентированные на соединение, быстрее на порядок, чем протоколы, ориентированные на соединение, — проверки данных на целостность и уведомление об их успешном приеме намного усложняют отправку даже небольших порций данных. Так что дейтаграммы удобны для передачи не очень важных данных, например, для приложений, аналогичных уже приводившимуся нами примеру с игрой: каждый игрок может использовать дейтаграммы, чтобы периодически отправлять информацию о своей позиции в игре всем другим игрокам по отдельности. Если один клиент пропускает пакет, то он быстро получает другой, что создает видимость непрерывной связи.

Корректное завершение работы

Корректное завершение работы характерно только для протоколов, ориентированных на соединение. При этом одна сторона инициирует завершение сеанса связи, а другая — все еще имеет возможность считывать данные, задержавшиеся в канале связи или сетевом стеке. Ориентированный на соединение протокол, не поддерживающий корректного завершения работы, немедленно завершает сеанс связи, игнорируя любые данные, которые не были считаны приемником.

При использовании TCP каждая сторона соединения должна сначала выполнить все необходимые операции, чтобы окончательно завершить сеанс связи. Сторона — инициатор завершения сеанса, отправляет партнеру дейтаграмму с управляющим флагом FIN. Получив эту дейтаграмму, партнер отправляет управляющий флаг АСК стороне-инициатору, чтобы подтвердить получение флага FIN, но все еще может отправлять данные. Флаг FIN озна-

ает, что инициатор завершения сеанса отправлять данные больше не будет. ак только партнер завершит отправку своих данных, он также отправит

Фаг FIN, получение которого инициатор подтверждает флагом АСК. После Го с е а н с связи считается полностью завершенным.