1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10...

148
Компьютерные сети - курс лекций Гуманитарный факультет 1 БЕЛОРУССКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ГУМАНИТАРНЫЙ ФАКУЛЬТЕТ Кафедра информационных технологий ЭЛЕКТРОННЫЙ УЧЕБНО-МЕТОДИЧЕСКИЙ КОМПЛЕКС ПО УЧЕБНОЙ ДИСЦИПЛИНЕ «КОМПЬЮТЕРНЫЕ СЕТИ » ЧАСТЬ 2 ДЛЯ СПЕЦИАЛЬНОСТИ «ПРИКЛАДНАЯ ИНФОРМАТИКА» 1-31 03 07 Составитель: В.В. Корнелюк, старший преподаватель кафедры информационных технологий Минск 2015

Upload: others

Post on 13-Jul-2020

6 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

1

БЕЛОРУССКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

ГУМАНИТАРНЫЙ ФАКУЛЬТЕТ

Кафедра информационных технологий

ЭЛЕКТРОННЫЙ УЧЕБНО-МЕТОДИЧЕСКИЙ КОМПЛЕКС

ПО УЧЕБНОЙ ДИСЦИПЛИНЕ

«КОМПЬЮТЕРНЫЕ СЕТИ »

ЧАСТЬ 2

ДЛЯ СПЕЦИАЛЬНОСТИ

«ПРИКЛАДНАЯ ИНФОРМАТИКА» 1-31 03 07

Составитель: В.В. Корнелюк, старший преподаватель кафедры

информационных технологий

Минск

2015

Page 2: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

2

СОСТАВ ЭУМК

1. Теоретический раздел

1.1. Курс лекций

2. Практический раздел

2.1. Практические задания

3. Раздел контроля знаний

3.1. Промежуточный контроль знаний

3.2. Итоговый контроль знаний

4. Вспомогательный раздел

4.1. Справочные материалы

4.2. Список учебной литературы и информационно-аналитических

материалов

Page 3: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

3

1. ТЕОРЕТИЧЕСКИЙ РАЗДЕЛ

1.1. КУРС ЛЕКЦИЙ - ЧАСТЬ 2

ОГЛАВЛЕНИЕ

ЛЕКЦИЯ 1

ЛЕКЦИЯ 2

ЛЕКЦИЯ 3

ЛЕКЦИЯ 4

ЛЕКЦИЯ 5

ЛЕКЦИЯ 6

ЛЕКЦИЯ 7

ЛЕКЦИЯ 8

ЛЕКЦИЯ 9

ЛЕКЦИЯ 10

ЛЕКЦИЯ 11

Page 4: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

4

ОСНОВЫ РАЗРАБОТКИ СЕТЕВЫХ ПРИЛОЖЕНИЙ

ЛЕКЦИЯ 1

Работа с именованными каналами

1. Введение

2. Организация каналов в Windows

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

4. Создание именованных каналов

5. Соединение сервера с клиентом

6. Соединение клиентов с именованным каналом

7. Обмен данными по именованному каналу

1. Введение

В области сетевого программирования имеется большой выбор различных

технологий. В данном курсе рассматриваются вопросы разработки сетевых

приложений в среде Windows с использованием интерфейса прикладного

программирования Win32 API.

В операционную систему Windows включена поддержка сетей с помощью

четырех базовых типов программного обеспечения: сервисы, API, протоколы и

драйверы сетевых адаптеров [2]. Они располагаются один над другим в виде

стека, и для каждого уровня предусмотрены интерфейсы, что дает возможность

создавать сетевые приложения на основе любого из них. Сетевые API

обеспечивают независимое от протоколов и сетевого оборудования

взаимодействие приложений через сеть. Поэтому технология

программирования на их основе представляется наиболее подходящей для тех

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

сетевого «железа» и перед которыми не ставится задача управления сетями.

2. Организация каналов в ОC Windows

Любое сетевое взаимодействие, если говорить концептуально,

представляет собой обмен данными между параллельными потоками. При

этом предполагается, что потоки выполняются в контекстах различных

процессов. Поскольку процессы изначально задумывались как обособленные

сущности, для обеспечения корректного взаимодействия процессов требуются

специальные средства и действия операционной системы.

Если потоки выполняются в разных процессах, они не могут обращаться к

общим переменным. Для обмена данными между ними операционная система

Page 5: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

5

создает некоторую передающую среду, аналог общей памяти,

представляющую собой разделяемый ресурс, к которому имеют доступ оба

потока ядра [1]. Если оформить этот разделяемый ресурс как общий файл, то

получим простейший канал передачи данных между процессами, в том числе

выполняющимися в разных компьютерах.

При этом ОС должна предоставить средства для генерации, именования,

установки режима доступа и атрибутов защиты каналов. Такой канал доступен

всем процессам, которые знают его имя и имеют необходимые привилегии.

Организация связи между процессами всегда предполагает установления

таких ее характеристик, как:

направление связи. Как рассматривалось ранее, связь бывает

однонаправленная (симплексная) и двунаправленная (полудуплексная для

поочередной передачи информации и дуплексная с возможностью

одновременной передачи данных в разных направлениях);

используемая модель передачи данных - потоковая или модель

сообщений;

объем передаваемой информации и сведения о том, обладает ли канал

буфером необходимого размера;

синхронность обмена данными. При передаче данных различают

синхронный и асинхронный обмен данными. Если поток-отправитель, отправив

сообщение, блокируется до получения этого сообщения потоком-адресатом, то

такое отправление сообщения называется синхронным. В противном случае

отправление сообщения называется асинхронным. Если поток-адресат

блокируется до тех пор, пока не получит сообщение, то такое получение

сообщения называется синхронным. В противном случае получение сообщения

называется асинхронным.

Обмен сообщениями называется синхронным, если поток-отправитель

синхронно передает сообщения, а поток-адресат синхронно принимает эти со-

общения. В противном случае обмен сообщениями называется асинхронным.

Предполагается, что обмен данными потоком всегда происходит синхронным

образом, т. к. в этом случае между отправителем и адресатом устанавливается

непосредственная связь.

Учитывая возможности синхронного обмена данными, этот механизм может

использоваться для решения задач синхронизации процессов. При этом следует

отметить, что в операционных системах Windows синхронизация процессов,

выполняющихся на разных компьютерах в локальной сети, может

осуществляться только при помощи механизма синхронного обмена данными.

Основной принцип работы канала состоит в буферизации вывода

одного процесса и обеспечении возможности чтения содержимого

программного канала другим процессом [1]. При этом интерфейс

Page 6: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

6

программного канала совпадает с интерфейсом обычного файла и реализуется

файловыми операциями read и write. Двумя основными моделями передачи

данных по каналу являются поток ввода-вывода и сообщения. При передаче в

рамках потоковой модели данные представляют собой неструктурированную

последовательность байтов и никак не интерпретируются системой. В модели

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

обычно их разделяют на сообщения заранее оговоренного формата.

Механизм генерации канала предполагает получение процессом-

создателем (процессом-сервером) двух дескрипторов для пользования этим

каналом. Один из дескрипторов применяется для чтения из канала, другой -

для записи в канал. Для общения с каналом процесс-клиент и процесс-сервер

должны иметь дескрипторы для чтения и записи. Процесс-сервер получает

дескриптор при создании канала. Процесс-клиент может получить дескриптор в

результате наследования, в том случае, когда клиент является потомком

сервера. Это типично для общения через так называемые анонимные каналы,

которые функционируют в пределах локальной вычислительной системы,

обеспечивают передачу данных между процессами, выполняющимися на одном

компьютере, и хорошо подходят для перенаправления выходного потока одной

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

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

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

описателей. Такой способ используется в сетевых взаимодействиях.

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

Именованным каналом называется объект ядра операционной системы,

который обеспечивает передачу данных между процессами, выполняющимися

на компьютерах в одной локальной сети [1].

Процесс, который создает именованный канал, называется сервером

именованного канала. Процессы, которые связываются с именованным каналом,

называются клиентами именованного канала.

Именованные каналы должны иметь уникальные в рамках сети имена

в соответствии с правилами именования ресурсов в сетях Windows (Universal

Naming Convention, UNC):

\\ServerName\pipe\PipeName

Для общения внутри одного компьютера имя записывается в форме

\\.\pipe\PipeName,

Page 7: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

7

где ServerName – сетевое имя компьютера, на котором выполняется

процесс-сервер; вместо сетевого имени может также использоваться его IP-

адрес. "." в этом поле вместо ServerName обозначает, что процесс-сервер, к

которому производится обращение, находится на данной локальной машине.

Слово "pipe" в составе имени фиксировано, а PipeName – имя канала,

задаваемое пользователем. Эти имена, подобно именам открытых файлов,

относятся к пространству имен под управлением драйверов файловых систем

именованных каналов (\Winnt\System32\Drivers\Npfs.sys).

Если клиент и сервер именованного канала работают на одном компьютере,

то для связи клиента с именованным каналом нужно вводить в качестве имени

сервера или точку, или полное имя компьютера. Если вместо имени компьютера

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

потоком, а не сообщениями.

Поскольку имена каналов интегрированы в общую структуру имен

объектов, а их интерфейс совпадает с интерфейсом файлов, приложения могут

открывать именованные каналы с помощью функции CreateFile и

взаимодействовать с ними через функции ReadFile и WriteFile.

Характеристики именованных каналов:

- они имеют имя, которое используется клиентами для связи с

именованным каналом;

- могут быть как полудуплексные, так и дуплексные;

- передача данных может осуществляться как потоком, так и сообщениями;

- обмен данными может быть как синхронным, так и асинхронным;

- возможна любая топология связей.

Потоковая модель и модель, ориентированная на сообщения, реализуются

на основе протоколов транспортного уровня ISO / OSI, которые

рассматривались ранее. Кратко повторим их, имея ввиду самый

распространенный стек протоколов TCP/IP.

Транспортные протоколы связи удаленных процессов, которые предназначены

для обмена сообщениями, - это протоколы без установления логического

соединения, или протоколы обмена дейтаграммами, поскольку само сообщение

здесь принято называть дейтаграммой. Каждое сообщение адресуется и посылается

процессом индивидуально. В стеке TCP/IP это протокол UDP.

Транспортные протоколы, которые поддерживают потоковую модель - это

протоколы, требующие установления логического соединения (в стеке TCP/IP это

протокол TCP). И в их основе лежит передача данных с помощью пакетов

информации. Операционные системы сами нарезают эти пакеты из передаваемого

потока данных, организуют правильную последовательность их получения и снова

Page 8: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

8

объединяют полученные пакеты в поток, так что с точки зрения взаимодействующих

процессов после установления логического соединения они имеют дело с потоковым

средством связи. Эти протоколы должны обеспечивать надежную связь.

Порядок работы с именованными каналами:

- создание именованного канала сервером:

- соединение сервера с экземпляром именованного канала;

- соединение клиента с экземпляром именованного канала;

- обмен данными по именованному каналу;

- отсоединение сервера от экземпляра именованного канала;

- закрытие именованного канала клиентом и сервером.

Рассмотрим функции, обеспечивающие создание и работу именованного

канала.

4. Создание именованных каналов [1]

Именованные каналы создаются процессом-сервером при помощи

функции CreateNamedPipe, которая имеет следующий прототип:

HANDLE CreateNamedPipe(

LPCTSTR lpName, // имя канала

DWORD dwOpenMode, // атрибуты

DWORD dwPipeMode, // режим передачи данных

DWORD nMaxInstances, // максимальное количество экземпляров канала

DWORD nOutBufferSize, // размер выходного буфера

DWORD nInBufferSize, // размер входного буфера

DWORD nDefaultTimeOut, // время ожидания связи с клиентом

LPSECURITY_ATTRIBUTES lpPipeAttributes // атрибуты безопасности

);

При удачном завершении функция CreateNamedPipe возвращает дескриптор

именованного канала, а в случае неудачи — одно из двух значений:

- INVALID_HANDLE_VALUE — неудачное завершение;

Page 9: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

9

- ERROR_INVALID_PARAMETR — значение параметра nMaxinstances больше,

чем величина PIPE_UNLIMITED_INSTANCES.

Опишем параметры этой функции.

Параметр lpName указывает на строку, которая должна иметь вид:

\\ .\pipe\pipe_name

Здесь "." (точка) обозначает локальную машину, т. к. новый именованный канал

всегда создается на локальной машине; слово pipe — фиксировано, a pipe_name

обозначает имя канала, которое задается пользователем и нечувствительно к

регистру.

Параметр dwOpenMode задает флаги, которые определяют направление

передачи данных, буферизацию, синхронизацию обмена данными и права доступа

к именованному каналу. Для определения направления передачи данных

используются флаги:

PIPE_ACCESS_DUPLEX — чтение и запись в канал;

PIPE_ACCESS_INBOUND — клиент пишет, а сервер читает данные

PIPE_ACCESS_OUTBOUND — сервер пишет, а клиент читает данные.

Флаг, определяющий направление передачи данных по именованному кана-

лу, должен совпадать для всех экземпляров одного и того же именованного

канала.

Для определения способа буферизации и синхронизации используются

флаги:

FILE_FLAG_WRITE_THROUGH — запрещает буферизацию при передаче

данных по сети;

FILE_FLAG_OVERLAPPED — разрешает асинхронную передачу данных по

каналу.

Эти флаги могут быть разными для каждого экземпляра одного и того же

именованного канала.

В этом же параметре могут быть заданы другие режимы доступа к имено-

ванному каналу.

Флаги, используемые для определения атрибутов безопасности,

PIPE_ACCESS_DUPLEX, PIPE_ACCESS_INBOUND и PIPE_ACCESS_OUTBOUND,

определяют специфические права доступа к именованному каналу, которые

включают следующие родовые права доступа:

- PIPE_ACCESS_DUPLEX — включает родовые права доступа GENERIC_READ,

GENERIC_WRITE и SYNCHRONIZE;

Page 10: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

10

- PIPE_ACCESS_INBOUND — включает родовые права доступа GENERIC_READ

и SYNCHRONIZE;

- PIPE_ACCESS_OUTBOUND — включает родовые права доступа GENERIC_WRITE

и SYNCHRONIZE.

Параметр dwPipeMode задает флаги, определяющие способ передачи

данных по именованному каналу. Для определения способов чтения и записи

данных в именованный канал используются флаги:

- PIPE_TYPE_BYTE — запись данных потоком;

- PIPE_TYPE_MESSAGE — запись данных сообщениями;

- PIPE_READMODE_BYTE — чтение данных потоком;

- PIPE_READMODE_MESSAGE — чтение данных сообщениями.

По умолчанию данные по именованному канату передаются потоком.

Флаги, определяющие способ чтения и записи данных в именованный канал,

должны совпадать для всех экземпляров одного и того же канала.

Для определения синхронизации доступа к именованному каналу используются

флаги:

- PIPE_WAIT — синхронная связь с каналом и обмен данными по каналу;

- PIPE_NOWAIT — асинхронная связь с каналом и обмен данными по каналу.

Эти флаги могут быть разными для каждого экземпляра именованного канала.

Параметр nMaxinstances определяет максимальное число экземпляров

именованного канала, которое может находиться в пределах от 1 до

PIPE_UNLIMITED__INSTANCES. Каждый экземпляр канала предназначен обмена

данными по каналу между сервером и отдельным клиентом.

Параметры nOutBuffersize и nInBuffersize определяют соответственно

размеры выходного и входного буферов для обмена данными по именованному

каналу. Однако эти значения рассматриваются операционными системами

Windows только как пожелания пользователя, а сам выбор размеров буферов

остается за операционной системой.

Параметр nDefaultTimeQut устанавливает время ожидания клиентом

связи с сервером. Это время используется при вызове клиентом функции

WaitNamedPipe, в которой параметр nTimeout имеет значение

NMPWAIT_USE_DEFAULT_WAIT.

Для связи сервера с несколькими клиентами по одному именованному

каналу сервер должен создать несколько экземпляров этого канала. Каждый

экземпляр именованного канала создается вызовом функции CreateNamedPipe,

Page 11: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

11

которая возвращает дескриптор экземпляра именованного канала. Отметим, что в

этом случае поток, создающий экземпляр именованного канала, должен иметь

право доступа FILE_CREATE_PIPE_INSTANCE К именованному каналу. Этим правом

по умолчанию обладает владелец именованного канала, т. е. тот процесс,

который создал этот именованный канал.

5. Соединение сервера с каналом [1]

После того как сервер создал именованный канал, он должен дождаться

соединения клиента с этим каналом. Для этого сервер вызывает функцию

ConnectNamedPipe, которая имеет следующий прототип:

BOOL ConnectNamedPipe(

HANDLE hNamedPipe, // дескриптор канала

LPOVERLAPPED lpOverlapped // асинхронная связь

) ;

В случае успешного завершения эта функция возвращает ненулевое значение,

а в случае неудачи — значение FALSE. Сервер использует эту функцию для связи

с клиентом по каждому свободному экземпляру именованного канала.

После окончания обмена данными с клиентом сервер может вызвать

функцию DisconnectNamedPipe, которая имеет следующий прототип:

BOOL DisconnectNamedPipe(

HANDLE hNamedPipe // дескриптор канала

) ;

и возвращает ненулевое значение в случае успеха или значение FALSE — в

случае неудачи. Эта функция разрывает связь сервера с клиентом. После этого

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

каналу, и поэтому любая операция доступа к именованному каналу со стороны

клиента вызовет ошибку. После разрыва связи с одним клиентом сервер снова

может вызвать функцию ConnectNamedPipe, чтобы установить связь по этому же

именованному каналу с другим клиентом.

6. Соединение клиентов с именованным каналом [1]

Прежде чем соединяться с именованным каналом, клиент может

попытаться определить, доступен ли какой-либо экземпляр этого канала для

соединения. С этой целью клиент должен вызвать функцию WaitNamedPipe,

которая имеет следующий прототип:

Page 12: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

12

BOOL WaitNamedPipe (

LPCTSTR lpNamedPipeName, // указатель на имя канала

DWORD nTimeOut // интервал ожидания

) ;

В случае успешного завершения эта функция возвращает ненулевое значе-

ние, а в случае неудачи — FALSE. Опишем параметры этой функции.

Параметр lpNamedPipeName указывает на строку, которая должна иметь

вид:

\\server_name\pipe\pipe_name

Здесь server_name обозначает имя компьютера, на котором выполняется

сервер именованного канала, слово pipe фиксировано, a pipe_name задает имя

именованного канала.

Параметр nTimeOut задает временной интервал, в течение которого клиент

ждет связь с сервером. Этот временной интервал определяется в миллисекундах

или может быть равен одному из следующих значений:

- NMPWAIT_USE_DEFAULT_WAIT — интервал времени ожидания определяется

значением параметра nDefauitTimeout, который задается в функции

CreateNamedPipe;

- NMPWAIT_WAIT_FOREVER — бесконечное время ожидания связи с имено-

ванным каналом.

Сделаем замечания относительно работы функции WaitNamedPipe.

1. Если заранее известно, что сервер вызвал функцию ConnectNamedPipe,

то функция соединения клиента с каналом CreateFile (см. далее) может

вызываться без предварительного вызова функции WaitNamedPipe.

2. Если не существует экземпляров именованного канала с именем

IpNamedPipe, то функция WaitNamedPipe немедленно завершается неудачей

независимо от времени ожидания, заданного параметром nTimeOut.

3. Если клиент соединяется с каналом до вызова сервером

ConnectNamedPipe, то функция WaitNamedPipe возвращает значение FALSE и

функция GetLastError вернет код ERROR_PIPE_CONNECTED. Поэтому

WaitNamedPipe нужно вызывать только после соединения сервера с каналом

посредством функции ConnectNamedPipe

Page 13: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

13

После того, как обнаружен свободный экземпляр канала, чтобы

установить связь с этим каналом, клиент должен вызвать функцию

CreateFile, которая имеет следующий прототип:

HANDLE CreateF i1е(

LPCTSTR lpFileName, // указатель на имя канала

DWORD dwDesiredAccess, // чтение или запись в канал

DWORD dwShareMode, // режим совместного использования

LPSECURITY_ATTRIBUTES IpSecurityAttributes, // атрибуты безопасности

DWORD dwCreationDisposition, // флаг открытия канала

DWORD dwFlagsAndAttributes, // флаги и атрибуты

HANDLE hTemplateFile // дополнительные атрибуты

);

В случае успешного завершения эта функция возвращает дескриптор имени

ванного канала, а в случае неудачи — значение INVALID_HANDLE_VALUE.

Здесь функция используется для открытия именованного канала, и ее

параметры могут принимать следующие значения:

Параметр lpFileName должен указывать на имя канала, которое быть

задано в том же формате, что и в функции WaitNamedPipe. Отметим, что если

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

именованного канала в функции СreateFile имя канала как \\.\pipe\pipe_name,

то файловая система именованных каналов открывает этот именованный канал в

режиме передачи данных потоком. Чтобы открыть именованный канал в

режиме передачи данных сообщениями, нужно задавать имя канала в виде:

\\server_name\pipe\pipe_name

Параметр dwDesiredAccess может принимать одно из следующих значений:

- 0 - разрешает получить атрибуты канала;

- GENERIC_READ — разрешает чтение из канала;

- GENERIC_WRITE — разрешает запись в канал.

Следует отметить, что функция СreateFile завершается неудачей, если

доступ к именованному каналу, заданный этими значениями, не соответствует

значениям параметра dwOpenMode в функции СreateNamedPipe.

Параметр dwShareMode определяет режим совместного использования име-

нованного канала и может принимать значение 0, которое запрещает совместное

использование именованного канала, или любую комбинацию следующих

значений:

- FILE_SHARE_READ — разрешает совместное чтение из канала;

Page 14: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

14

- FILE_SHARE_WRITE — разрешает совместную запись в канал.

Параметр lpSecurityAttributes задает атрибуты безопасности именованно-

го канала. Пока значение этого параметра будем устанавливать в NULL.

Для именованного канала параметр dwCreationDisposition должен быть

равен значению OPEN_EXISTING, т. к. клиент всегда открывает существующий

именованный канал.

Для именованного канала параметр dwFlagsAndAttributes можно задать

равным 0, что определяет флаги и атрибуты по умолчанию.

Значение параметра hTemplateFile задается равным NULL.

Несмотря на то, что функция WaitNamedPipe может успешно завершиться,

последующий вызов функции CreateFiie может завершиться неудачей по

следующим причинам: между вызовами этих функций сервер закрыл канал или

между вызовами функций другой клиент связался с экземпляром этого канала.

Для предотвращения последней ситуации сервер должен создавать новый

экземпляр именованного канала после каждого успешного завершения функции

ConnectNamedPipe или создать сразу несколько экземпляров именованного

канала.

7. Обмен данными по именованному каналу [1]

Для обмена данными по именованному каналу используются функции

ReadFile и WriteFile. Так как в случае именованного канала разрешен

асинхронный обмен данными, то в функциях ReadFile и WriteFile может

использоваться параметр lpOverlapped при том условии, что в вызове функции

CreateNamedPipe в параметре dwOpenMode был установлен флаг

FILE_FLAG_OVERLAPPED. Максимально в именованный канал может быть

записано до 65 535 байт одной операцией WriteFile. Кроме того, отметим, что

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

использоваться также функции ReadFileEx и WriteFleEx.

После завершения обмена данными по именованному каналу потоки долж-

ны закрыть дескрипторы экземпляров именованного канала, используя функцию

CloseHandle.

Теперь перейдем к примерам программ, в которых показано, как обмени-

ваться данными по именованному каналу. Сначала рассмотрим пример, в

котором процесс-сервер создает именованный канал, а затем ждет, пока

клиент не соединится с именованным каналом. После этого сервер читает

из именованного канала десять чисел и выводит их на консоль.

В листинге 1 приведена программа процесса-сервера именованного

канала.

Page 15: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

15

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

PipeSrvTestApp.cpp

В листинге 2 приведем программу процесса – клиента именованного

канала, который сначала связывается с именованным каналом, а затем

записывает в него десять чисел.

Листинг 2. Процесс – клиент именованного канала - Приложение

PipeTestApp.cpp

ЛЕКЦИЯ 2

Работа с именованными каналами

1. Общая организация сетевого взаимодействия в Windows

2. Программы сервера и клиента, обменивающихся сообщениями

1. Общая организация сетевого взаимодействия в Windows

Как упоминалось в предыдущей лекции, к основным способам

межпроцессного обмена относят каналы. Анонимные каналы поддерживают

потоковую модель, в рамках которой данные представляют собой

неструктурированную последовательность байтов, и используются для

межпроцессного обмена в пределах одного компьютера. Именованные каналы,

поддерживающие как потоковую модель, так и модель, ориентированную на

сообщения, обеспечивают обмен данными не только в изолированной

вычислительной среде, но и в локальной сети.

Основной принцип работы канала состоит в буферизации вывода одного

процесса и обеспечении возможности чтения содержимого программного

канала другим процессом. Даже если процесс посылает данные самому себе,

они проходят через ядро. При этом интерфейс программного канала

совпадает с интерфейсом файла и реализуется обычными файловыми

операциями read и write. Следовательно, для организации таких каналов, а

также их именования в ядре должны быть созданы элементы файловой

системы. Фактически именованные каналы реализованы как файловые

системы NPFS (Named Pipes File System).

Механизм генерации канала предполагает создание процессом-сервером

двух дескрипторов для пользования этим каналом. Один из дескрипторов

применяется для чтения из канала, другой - для записи в канал.

При создании именованного канала ему назначается уникальное имя,

определяется максимальное количество одновременных соединений с

клиентами канала и режим работы канала (должен ли канал быть

Page 16: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

16

односторонним или двусторонним - дуплексным, ведется ли передача пакетами

или потоком байтов). При передаче пакетами данные одной операции записи

отделяются в буфере канала от данных другой операции записи.

Для каждого процесса-клиента канала создается свой экземпляр канала, с

собственными буферами и дескрипторами и с собственным механизмом

передачи данных, не влияющим на остальные экземпляры. Экземпляры одного

канала имеют общее имя, указанное при создании; сервер назначает имя канала

в соответствии с универсальными правилами именования (Universal Naming

Convention, UNC), которые обеспечивают независимый от протоколов способ

идентификации каналов в Windows-сетях.

Именованные каналы широко используются внутри самой системы

Windows. Например, взаимодействие менеджера сервисов с самими сервисами

осуществляется через несколько именованных каналов. Для связи с

планировщиком событий и с сервером локальной аутентификации также

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

Если говорить об общей организации сетевого взаимодействия в

Windows, то организаторы данных в сети – это транспортные протоколы.

Транспортный протокол определяет, каким образом данные будут

представлены на следующий, принимающий уровень, и соответствующим

образом их упаковывает. Он передаѐт данные драйверу платы сетевого адаптера

и редиректору.

Редиректоры «переназначают» сетевым сервисам другого компьютера

локальные запросы на сетевые ресурсы. Другими словами, редиректор - это

программная компонента, через которую один компьютер получает доступ к

другому компьютеру. Редиректор взаимодействует со стеками протоколов, к

которым он привязан через свой интерфейс. В задачи редиректора входит также

восстановление соединений после их разрыва.

Клиентская часть программного обеспечения Windows работает по

следующему алгоритму:

1.Процесс для открытия соединения обращается к менеджеру ввода-вывода

с соответствующим запросом

2.Менеджер ввода-вывода определяет, что сделанный запрос адресован на

удалѐнный компьютер, и передаѐт его редиректору.

3. Редиректор передаѐт запрос низкоуровневым драйверам сетевой карты,

которые в свою очередь передают этот запрос на удалѐнный сервер.

4. Сервер поддерживает соединения, запрошенные редиректорами

клиентской стороны, и обеспечивает им доступ к запрошенным ресурсам.

Page 17: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

17

Когда сервер получает с удалѐнной рабочей станции запрос на чтение

файла, расположенного на сервере, то происходит следующее:

1. Низкоуровневые сетевые драйверы получают запрос и передают его

драйверу сервера.

2. Сервер передаѐт запрос на чтение файла соответствующему драйверу

локальной файловой системы.

3. Для доступа к файлу драйвер локальной файловой системы обращается к

низкоуровневому драйверу диска.

4. Полученные данные передаются обратно драйверу локальной

файловой системы.

5. Драйвер локальной файловой системы передаѐт данные серверу.

6. Сервер передаѐт данные низкоуровневым сетевым драйверам для

передачи на компьютер клиента.

Помимо редиректоров, Windows включает в свой состав два других

сетевых компонента, обеспечивающих соединения с другими компьютерами, -

NЕТВIOS и сокеты Windows. Оба эти компонента входят в механизм

межпроцессных коммуникаций и используются для приложений клиент-

сервер. Кроме того, в состав Windows включены еще два механизма

межпроцессных коммуникаций в виде почтовых ящиков (слотов - mailslots) и

вызова удалѐнной процедуры RРС.

2. Программы сервера и клиента, обменивающихся сообщениями [1]

Рассмотрим программу процесса-сервера именованного канала, который

сначала создает именованный канал, затем ждет подключения к нему клиента.

После этого сервер принимает от клиента одно сообщение, выводит это

сообщение на консоль и посылает клиенту ответное сообщение.

Листинг 1. Сервер именованного канала, отвечающий на сообщение

клиента – Приложение 16-03- Server.cpp

#include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

HANDLE hNamedPipe;

DWORD dwBytesRead; // для количества прочитанных байтов

DWORD dwBytesWrite; // для количества записанных байтов

char pchMessage[80]; // для сообщения

int nMessageLength; // длина сообщения

Page 18: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

18

// создаем именованный канал для чтения и записи

hNamedPipe = CreateNamedPipe(

"\\\\.\\pipe\\demo_pipe", // имя канала

PIPE_ACCESS_DUPLEX, // читаем из канала и пишем в канал

PIPE_TYPE_MESSAGE | PIPE_WAIT, // синхронная передача сообщений

1, // максимальное количество экземпляров канала

0, // размер выходного буфера по умолчанию

0, // размер входного буфера по умолчанию

INFINITE, // клиент ждет связь бесконечно долго

NULL // безопасность по умолчанию

);

// проверяем на успешное создание

if (hNamedPipe == INVALID_HANDLE_VALUE)

{

cerr << "Create named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// ждем, пока клиент свяжется с каналом

cout << "The server is waiting for connection with a client." << endl;

if(!ConnectNamedPipe(

hNamedPipe, // дескриптор канала

NULL // связь синхронная

))

{

cerr << "Connect named pipe failed." << endl

<< "The last error code: "<<GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// читаем сообщение от клиента

if (!ReadFile(

hNamedPipe, // дескриптор канала

pchMessage, // адрес буфера для ввода данных

sizeof(pchMessage), // количество читаемых байтов

&dwBytesRead, // количество прочитанных байтов

NULL)) // передача данных синхронная

{

cerr << "Data reading from the named pipe failed." << endl

<< "The last error code: "<< GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим полученное от клиента сообщение на консоль

Page 19: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

19

cout << "The server received the message from a client: "

<< endl << '\t' << pchMessage << endl;

// вводим строку

cout << "Input a string: ";

cin.getline(pchMessage, 80);

// определяем длину строки

nMessageLength = strlen(pchMessage) + 1;

// отвечаем клиенту

if (!WriteFile(

hNamedPipe, // дескриптор канала

pchMessage, // адрес буфера для вывода данных

nMessageLength, // количество записываемых байтов

&dwBytesWrite, // количество записанных байтов

NULL // передача данных синхронная

))

{

cerr << "Write file failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим посланное клиенту сообщение на консоль

cout << "The server sent the message to a client: "

<< endl << '\t' << pchMessage << endl;

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

В листинге 2 приведена программа процесса-клиента именованного канала,

который сначала вводит с консоли имя компьютера в локальной сети, на

котором запущен сервер именованного канала. Затем связывается с этим

именованным каналом. После этого клиент передает серверу одно сообщение,

получает от него ответное сообщение и выводит на консоль.

Листинг 2. Процесс – клиент именованного канала, обменивающийся

сообщением с сервером - Приложение 16-04-Client.cpp

#include <windows.h>

#include <iostream.h>

#include <string.h>

Page 20: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

20

int main()

{

char machineName[80];

char pipeName[80];

HANDLE hNamedPipe;

DWORD dwBytesWritten; // для количества записанных байтов

DWORD dwBytesRead; // для количества прочитанных байтов

char pchMessage[80]; // для сообщения

int nMessageLength; // длина сообщения

// вводим имя машины в сети, на которой работает сервер

cout << "Enter a name of the server machine: ";

cin >> machineName;

// подставляем имя машины в имя канала

wsprintf(pipeName, "\\\\%s\\pipe\\demo_pipe", machineName);

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_READ | GENERIC_WRITE, // читаем и записываем в канал

FILE_SHARE_READ | FILE_SHARE_WRITE, // разрешаем чтение и запись

NULL, // безопасность по умолчанию

OPEN_EXISTING, // открываем существующий канал

FILE_ATTRIBUTE_NORMAL, // атрибуты по умолчанию

NULL); // дополнительных атрибутов нет

// проверяем связь с каналом

if (hNamedPipe==INVALID_HANDLE_VALUE)

{

cerr << "Connection with the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// вводим строку

cin.get();

cout << "Input a string: ";

cin.getline(pchMessage, 80);

// определяем длину строки

nMessageLength = strlen(pchMessage) + 1;

// пишем в именованный канал

if (!WriteFile(

hNamedPipe, // дескриптор канала

pchMessage, // данные

nMessageLength, // размер данных

&dwBytesWritten, // количество записанных байтов

NULL)) // синхронная запись

{

Page 21: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

21

// ошибка записи

cerr << "Write file failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим посланное сообщение на консоль

cout << "The client sent the message to a server: "

<< endl << '\t' << pchMessage << endl;

// читаем из именованного канала

if (!ReadFile(

hNamedPipe, // дескриптор канала

pchMessage, // данные

sizeof(pchMessage), // размер данных

&dwBytesRead, // количество записанных байтов

NULL)) // синхронное чтение

{

// ошибка чтения

cerr << "Read file failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим полученное сообщение на консоль

cout << "The client received the message from a server: "

<< endl << '\t' << pchMessage << endl;

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

В рассмотренных в лекциях 1 и 2 примерах программ атрибуты

безопасности именованного канала устанавливались по умолчанию. При

установке по умолчанию эти атрибуты устанавливаются таким образом, что

канал принадлежит только пользователю, создавшему этот канал. Отсюда

следует, что вход на компьютеры и сервера, и клиента должен осуществляться

с одинаковыми логинами и паролями, что идентифицирует клиент-серверное

взаимодействие одного и того же пользователя. Теперь приведем текст

программы сервера 16-05-Server.cpp, которая создает именованный

канал, доступный любому пользователю. Сервер создает общедоступный

Page 22: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

22

канал, ждет соединения клиента, читает сообщение и отвечает клиенту, после

чего закрывается.

См. приложение 16-05-Server.cpp.

ЛЕКЦИЯ 3

Работа с именованными каналами

2. Список функций для работы с именованными каналами

3. Копирование данных из именованного канала

1. Список функций для работы с именованными каналами

Для работы с именованными каналами Windows API предоставляет

следующие функции:

CreateNamedPipe

Создание именованного канала или нового

экземпляра канала. Функция доступна только

серверу.

CreateFile

Подключение к экземпляру именованного

канала со стороны клиента. Функция доступна

только клиенту.

WaitNamedPipe

Ожидание клиентом появления свободного

экземпляра именованного канала для подключения к

нему.

ConnectNamedPipe Ожидание сервером подключения клиента к

экземпляру именованного канала.

ReadFile, Чтение данных из именованного канала.

Функция доступна как клиенту, так и серверу.

WriteFile, WriteFileEx Запись данных в именованный канал.

Функция доступна как клиенту, так и серверу.

PeekNamedPipe

Чтение данных из именованного канала без

удаления прочитанных данных из буфера канала.

Функция доступна как клиенту, так и серверу.

TransactNamedPipe

Запись и чтение из именованного канала

одной операцией. Функция доступна как клиенту,

так и серверу.

Page 23: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

23

DisconnectNamedPipe Отсоединение сервера от экземпляра

именованного канала.

GetNamedPipeInfo Получение информации об именованном

канале.

GetNamedPipeHandleS

tate

Получение текущего режима работы

именованного канала и количества созданных

экземпляров канала.

SetNamedPipeHandleSt

ate

Установка текущего режима работы

именованного канала.

CloseHandle

Закрытие дескриптора экземпляра

именованного канала, освобождение связанных с

объектом ресурсов.

2. Копирование данных из именованного канала [1]

Для копирования данных из именованного канала используется функция

PeekNamedPipe, которая копирует данные в буфер, не удаляя их из канала.

Эта функция имеет следующий прототип:

BOOL PeekNamedPipe(

HANDLE hNamedPipe, \\ дескриптор канала

LPVOID lpBuffer, \\ буфер данных

DWORD nBufferSize, \\ размер буфера данных

LPDWORD lpBytesRead, \\ количество прочитанных байтов

LPDWORD lpTotalBytesAvail, \\ количество доступных байтов

LPDWORD lpBytesLeftThisMessage \\ количество непрочитанных

байтов

);

В случае успешного завершения функция возвращает ненулевое

значение, а в случае неудачи – FALSE. Если данных в канале нет, то функция

немедленно возвращает управление и устанавливает в ноль значения

переменных, на которые указывают параметры lpBytesRead, lpTotalBytesAvail

и lpBytesLeftThisMessage.

Параметры функции PeekNamedPipe - дескриптор именованного

канала (должен быть открыт в режиме чтения); область памяти, куда будут

читаться данные из канала ( если установлены в NULL, то данные читаться не

будут); размер этой области памяти; количество прочитанных байт (если

данные не читаются, то - NULL); количество доступных байт в канале;

количество непрочитанных байт из сообщения.

Page 24: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

24

Если данные передаются потоком, то функция читает ровно столько

байтов данных, какова длина буфера данных. Если же данные передаются

сообщениями, то функция читает полностью сообщение, которое входит в

буфер данных. В противном случае читается часть сообщения, а количество

непрочитанных байтов возвращается через переменную, на которую

указывает параметр lpBytesLeftThisMessage.

В листинге 1 приведена программа процесса-клиента именованного

канала, который использует функцию PeekNamedPipe для копирования

данных из канала.

Листинг 1. Клиент именованного канала, копирующий данные из

канала – Приложение 16-06- PeekNamedPipe.cpp

#include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

char machineName[80];

char pipeName[80];

HANDLE hNamedPipe;

DWORD dwBytesRead; // для количества прочитанных байтов

DWORD dwTotalBytesAvail; // количество байтов в канале

DWORD dwBytesLeftThisMessage; // количество непрочитанных байтов

char pchMessage[80]; // для сообщения

// вводим имя машины в сети, на которой работает сервер

cout << "Enter a name of the server machine: ";

cin >> machineName;

cin.get();

// подставляем имя машины в имя канала

wsprintf(pipeName, "\\\\%s\\pipe\\demo_pipe", machineName);

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_READ | GENERIC_WRITE, // читаем и записываем в канал

FILE_SHARE_READ | FILE_SHARE_WRITE, // разрешаем чтение и запись

NULL, // безопасность по умолчанию

OPEN_EXISTING, // открываем существующий канал

FILE_ATTRIBUTE_NORMAL, // атрибуты по умолчанию

NULL); // дополнительных атрибутов нет

// проверяем связь с каналом

if (hNamedPipe==INVALID_HANDLE_VALUE)

{

cerr << "Connection with the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

Page 25: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

25

cout << "Press any key to exit.";

cin.get();

return 0;

}

// ждем команду на копирование сообщения из канала

cout << "Press any key to peek a message." << endl;

cin.get();

// копируем информацию из именованного канала

if (!PeekNamedPipe(

hNamedPipe, // дескриптор канала

pchMessage, // данные

sizeof(pchMessage), // размер данных

&dwBytesRead, // количество записанных байтов

&dwTotalBytesAvail, // количество байтов в канале

&dwBytesLeftThisMessage // количество непрочитанных байтов

))

{

// ошибка чтения сообщения

cerr << "Peek named pipe failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим полученное сообщение на консоль

if (dwTotalBytesAvail)

cout << "The peeked message: "

<< endl << '\t' << pchMessage << endl;

else

cout << "There is no mesage." << endl;

// теперь читаем сообщение из именованного канала

if (!ReadFile(

hNamedPipe, // дескриптор канала

pchMessage, // данные

sizeof(pchMessage), // размер данных

&dwBytesRead, // количество записанных байтов

NULL)) // синхронное чтение

{

// ошибка чтения

cerr << "Read file failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

Page 26: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

26

// выводим полученное сообщение на консоль

cout << "The client received the message from a server: "

<< endl << '\t' << pchMessage << endl;

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit." << endl;

cin.get();

return 0;

}

ЛЕКЦИЯ 4

Работа с именованными каналами

1. Передача транзакций по именованному каналу

1. Передача транзакций по именованному каналу [1]

Кроме рассмотренных в предыдущих лекциях функций передачи данных,

для обмена сообщениями по сети может также использоваться функция

TransactNamedPipe, которая объединяет операции записи и чтения в одну

операцию, которая называется транзакцией. Отметим, что функция

TransactNamedPipe может использоваться только в том случае, если сервер

при создании именованного канала установил флаг PIPE_TYPE_MESSAGE.

Функция TransactNamedPipe имеет следующий прототип:

BOOL TransactNamedPipe (

HANDLE hNamedPipe, // дескриптор именованного канала

LPVOID lpInBuffer, // буфер для записи в канал

DWORD dwInBufferSize, // длина буфера для записи

LPVOID lpOutBuffer, // буфер для чтения из канала

DWORD dwOutBufferSize, // длина буфера для чтения

LPDWORD lpBytesRead, // количество прочитанных байтов

LPOVERLAPPED lpOverlapped // асинхронный доступ к каналу

);

В случае успешного завершения функция TransactNamedPipe возврашает

ненулевое значение, а в случае неудачи — FALSE.

Параметры функции имеют следующее назначение:

Page 27: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

27

В параметре hNamedPipe должен быть установлен дескриптор

именованного канала.

Параметр lpInBuffer должен указывать на буфер, из которого

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

Параметр dwInBufferSize должен содержать длину передаваемого

сообщения в байтах.

Параметр lpOutBuffer должен указывать на буфер, в который читаются

данные из именованного канала.

Параметр dwOutBufferSize должен содержать длину буфера, в который

читается сообщение.

Параметр lpBytesRead должен указывать на переменную типа DWORD, в

которую функция поместит количество прочитанных байтов. Если осуществ-

ляется асинхронный доступ к именованному каналу, то значение этого

параметра можно установить в NULL.

Параметр lpOverlapped используется в том случае, если осуществляется

асинхронный доступ к именованному каналу. В этом случае параметр должен

указывать на структуру типа OVERLAPPED. Будем устанавливать этот параметр в

NULL, что задает синхронную передачу данных.

В листинге 1 приведена программа процесса-клиента именованного

канала, которая передает транзакцию, используя функцию

TransactNamedPipe.

Напомним, что если клиент и сервер именованного канала работают на

одном компьютере, то для связи клиента с именованным каналом нужно

вводить полное имя компьютера. Если же вместо имени компьютера будет

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

потоком, а не сообщениями. Это, в свою очередь, вызовет ошибку при передаче

данных.

Листинг 1. Пример передачи транзакции по именованному каналу

(клиент) – Приложение 16- 07- TransactNamedPipe.cpp

#include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

char machineName[80];

char pipeName[80];

HANDLE hNamedPipe;

DWORD dwBytesRead; // для количества прочитанных байтов

char pchInBuffer[80]; // для записи сообщения

char pchOutBuffer[80]; // для чтения сообщения

Page 28: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

28

int nMessageLength; // длина сообщения

// вводим имя машины в сети, на которой работает сервер

cout << "Enter a name of the server machine: ";

cin >> machineName;

// подставляем имя машины в имя канала

wsprintf(pipeName, "\\\\%s\\pipe\\demo_pipe", machineName);

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_READ | GENERIC_WRITE, // читаем и записываем в канал

FILE_SHARE_READ | FILE_SHARE_WRITE, // разрешаем чтение и запись

NULL, // безопасность по умолчанию

OPEN_EXISTING, // открываем существующий канал

FILE_ATTRIBUTE_NORMAL, // атрибуты по умолчанию

NULL); // дополнительных атрибутов нет

// проверяем связь с каналом

if (hNamedPipe==INVALID_HANDLE_VALUE)

{

cerr << "Connection with the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// вводим строку

cin.get();

cout << "Input a string: ";

cin.getline(pchInBuffer, 80);

// определяем длину строки

nMessageLength = strlen(pchInBuffer) + 1;

// пишем и читаем из именованного канала одной транзакцией

if (!TransactNamedPipe(

hNamedPipe, // дескриптор канала

&pchInBuffer, // адрес входного буфера канала

nMessageLength, // длина входного сообщения

&pchOutBuffer, // адрес выходного буфера канала

sizeof(pchOutBuffer), // длина выходного буфера канала

&dwBytesRead, // количество прочитанных байтов

NULL)) // передача транзакции синхронная

{

// ошибка транзакции

cerr << "Transact named pipe failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

Page 29: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

29

return 0;

}

// выводим посланное сообщение на консоль

cout << "The sent message: "

<< endl << '\t' << pchInBuffer << endl;

// выводим полученное сообщение на консоль

cout << "The received message: "

<< endl << '\t' << pchOutBuffer << endl;

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

ЛЕКЦИЯ 5

Работа с именованными каналами

Определение и изменение состояния именованного канала

1. Определение состояния именованного канала

2. Изменение состояния именованного канала

1. Определение состояния именованного канала

Для получения информации о состоянии именованного канала

используется функция GetNamedPipeHandlestate, которая имеет следующий

прототип:

BOOL GetNamedPipeHandleState(

HANDLE hNamedPipe, // дескриптор именованного канала

LPDWORD IpState, // состояние канала

LPDWORD IpCurrentInstances, // количество экземпляров канала

LPDWORD lpMaxCollectionCount, // максимальное количество байтов

LPDWORD lpCollectionDataTimeout, // интервал ожидания

LPTSTR lpUserName, // имя клиента именованного канала

DWORD dwMaxUserNameSize // длина буфера для имени клиента

В случае успешного завершения функция GetNamedPipeHandlestate воз-

вращает ненулевое значение, а в случае неудачи — FALSE.

Параметры функции имеют следующее назначение.

Page 30: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

30

В параметре hNamedPipe должен быть установлен дескриптор

именованного канала. Причем канал должен быть открыт в режиме чтения.

Параметр lpstate должен указывать на переменную типа DWORD, в

которую функция записывает любую комбинацию следующих значений:

□ PIPE_NOWAIT — канал не блокирован;

□ PIPE_READMODE_MESSAGE — канал открыт в режиме передачи данных

сообщениями.

Если значение PIPE_NOWAIT не установлено, то канал блокирован. Если не

установлено значение PIPE_READMODE_MESSAGE, то канал открыт в режиме

передачи данных потоком. Если определять состояние именованного каната не

нужно, то в параметре lpstate может быть установлено значение NULL

Параметр lpCurrentInstances должен указывать на переменную типа

DWORD, в которую функция записывает количество созданных экземпляров

именованного канала. Если эта информация не нужна, то значение этого

параметра может быть установлено в NULL.

Параметр lpMaxCollectionCount должен указывать на переменную типа

DWORD, в которую функция запишет максимальное количество байтов, которые

клиент именованного канала должен записать в этот канал, прежде чем данные

будут переданы серверу канала. Этот параметр должен быть установлен в

NULL, если функция вызывается сервером именованного канала или если клиент

и сервер работают на одном компьютере и для связи с сервером клиент

использует символ "." вместо имени сервера. Если информация о

максимальном количестве байтов не нужна, то в этом параметре может быть

установлено значение NULL.

Параметр lpCollectionDataTimeout должен указывать на переменную

DWORD, в которую функция поместит количество миллисекунд, которые могут

пройти, прежде чем данные могут быть переданы по сети. Как и в предыдущем

случае, этот параметр должен быть установлен в NULL в том случае, если

функция вызывается сервером именованного канала или клиент и сервер

работают на одном компьютере и для связи с сервером клиент использует

символ "." вместо имени сервера. Если информация об интервале задержки

перед передачей данных не нужна, то в этом параметр может быть установлено

значение NULL.

Параметр lpUserName должен указывать на символьный массив, в котором

функция поместит строку с именем владельца именованного канала. Имя

владельца можно получить только в том случае, если доступ к каналу открыт

всем пользователям. Если информация об имени владельца канала не нужна,

то этот параметр может быть установлен в NULL.

Параметр dwMaxUserNamesize должен содержать размер области памяти,

на которую указывает параметр lpuserName.

Page 31: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

31

В листинге 1 приведена программа процесса-клиента именованного

канала, которая получает информацию о состоянии именованного канала

посредством вызова функции GetNamedPipeHandleState.

Листинг 1. Пример определения состояния именованного канала –

Приложение 16-09- GetNamedPipeHandlestate.cpp

#include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

char machineName[80];

char pipeName[80];

HANDLE hNamedPipe;

DWORD dwState; // состояние канала

DWORD dwCurInstances; // количество экземпляров канала

DWORD dwMaxCollectionCount; // размер буфера клиента канала

DWORD dwCollectDataTimeout; // задержка перед передачей данных

TCHAR chUserName[255]; // имя владельца именованного канала

// вводим имя машины в сети, на которой работает сервер

cout << "Enter a name of the server machine: ";

cin >> machineName;

cin.get();

// подставляем имя машины в имя канала

wsprintf(pipeName, "\\\\%s\\pipe\\demo_pipe", machineName);

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_READ | GENERIC_WRITE, // читаем и записываем в канал

FILE_SHARE_READ | FILE_SHARE_WRITE, // разрешаем чтение и запись

// в канал

NULL, // безопасность по умолчанию

OPEN_EXISTING, // открываем существующий канал

FILE_ATTRIBUTE_NORMAL, // атрибуты по умолчанию

NULL); // дополнительных атрибутов нет

// проверяем связь с каналом

if (hNamedPipe==INVALID_HANDLE_VALUE)

{

cerr << "Connection with the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

Page 32: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

32

}

// определяем состояние канала

if (!GetNamedPipeHandleState(

hNamedPipe, // дескриптор именованного канала

&dwState, // состояние именованного канала

&dwCurInstances, // количество экземпляров канала

&dwMaxCollectionCount, // размер буфера клиента канала

&dwCollectDataTimeout, // макс. задержка перед передачей данных

chUserName, // имя пользователя канала

255)) // максимальная длина имени

{

cerr << "Get named pipe handle state failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим состояние канала на консоль

cout << "State: ";

switch (dwState)

{

case (PIPE_NOWAIT):

cout << "PIPE_NOWAIT" << endl;

break;

case (PIPE_READMODE_MESSAGE):

cout << "PIPE_READMODE_MESSAGE" << endl;

break;

case (PIPE_NOWAIT | PIPE_READMODE_MESSAGE):

cout << "PIPE_NOWAIT and PIPE_READMODE_MESSAGE" << endl;

break;

default:

cout << "Unknown state." << endl;

break;

}

cout << "Current instances: " << dwCurInstances << endl

<< "Max collection count: " << dwMaxCollectionCount << endl

<< "Collection data timeout: " << dwCollectDataTimeout << endl

<< "User name: " << chUserName << endl;

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

Page 33: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

33

2. Изменение состояния именованного канала

Изменить некоторые характеристики именованного канала можно

посредством функции SetNamedPipeHandleState, которая имеет следующий

прототип:

BOOL SetNamedPipeHandleState (

HANDLE hNamedPipe, // дескриптор именованного канала

LPDWORD lpMode, // новый режим передачи данных

PDWORD lpMaxCollectionCount, // максимальное количество байтов

LPDWORD lpCollectionDataTimeout // интервал ожидания

В случае успешного завершения функция SetNamedPipeHandleState

возвращает ненулевое значение, а в случае неудачи — FALSE.

Параметры функции имеют следующее назначение.

В параметре hNamedPipe должен быть установлен дескриптор

именованного канала. Причем канал должен быть открыт в режиме записи.

Параметр lpMode должен указывать на переменную типа DWORD, которая

содержит новые режимы работы именованного канала.

Возможно изменить следующие режимы работы именованного

канала: режим передачи данных и режим ожидания при выполнении записи

или чтении данных в именованный канал, а также ожидания сервером

соединения клиента с именованным каналом.

Режим передачи данных может принимать следующие значения:

PIPE_READMODE_BYTE — передача данных потоком;

PIPE_READMODE_MESSAGE — передача данных сообщениями.

Режим ожидания может принимать следующие значения:

PIPE_WAIT — блокирование приложения до завершения

выполнения функций ConnectNamedPipe, WriteFile и ReadFile;

PIPE_NOWAIT — выполнение функций ConnectNamedPipe,

WriteFile и ReadFile не блокирует работу приложения.

Отметим, что эти режимы ожидания не влияют на работу асинхронных

операций доступа к именованному каналу, а предназначены только для работы

менеджера локальной сети. В параметре lpMode может быть установлена любая

комбинация флагов режимов передачи данных и ожидания или только один из

этих флагов. Если режимы работы именованного канала не изменяются, то

параметр lpMode должен содержать значение NULL.

Параметр lpMaxCollectionCount должен указывать на переменную типа

DWORD, которая содержит максимальное количество байтов, которые клиент

Page 34: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

34

именованного канала может записать в этот канал, прежде чем эти данные

будут переданы серверу канала. Однако если клиент открыл именованный канал

в режиме FILE_FLAG_WRITE_THROUGH, то параметр lpMaxCollectionCount

игнорируется. Этот параметр должен быть установлен в NULL, если функция не

изменяет размер максимального количества записываемых перед передачей

байтов.

Параметр lpCollectionDataTimeout должен указывать на переменную

типа DWORD, которая содержит новое количество миллисекунд, которые могут

пройти, прежде чем данные могут быть переданы по сети. Как и в предыдущем

случае, если клиент открыл именованный канал в режиме

FILE_FLAG_WRITE_THROUGH, то параметр lpCollectionDataTimeout игнорируется.

Этот параметр должен быть установлен в NULL, если функция не изменяет интервал

ожидания до передачи данных по именованному каналу.

В листинге 2 приведена программа процесса-клиента именованного ка-

нала, которая изменяет состояние именованного канала посредством вызова

функции SetNamedPipeHandleState.

Листинг 2. Пример изменения состояния именованного канала –

Приложение SetNamedPipeHandleState.cpp

#include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

char machineName[80];

char pipeName[80];

HANDLE hNamedPipe;

DWORD dwState; // состояние канала

DWORD dwCurInstances; // количество экземпляров канала

DWORD dwMaxCollectionCount; // размер буфера клиента канала

DWORD dwCollectDataTimeout; // временная задержка перед передачей данных

TCHAR chUserName[255]; // имя владельца именованного канала

// вводим имя машины в сети, на которой работает сервер

cout << "Enter a name of the server machine: ";

cin >> machineName;

cin.get();

// подставляем имя машины в имя канала

wsprintf(pipeName, "\\\\%s\\pipe\\demo_pipe", machineName);

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

Page 35: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

35

GENERIC_READ | GENERIC_WRITE, // читаем и записываем в канал

FILE_SHARE_READ | FILE_SHARE_WRITE, // разрешаем чтение и запись

NULL, // безопасность по умолчанию

OPEN_EXISTING, // открываем существующий канал

FILE_ATTRIBUTE_NORMAL, // атрибуты по умолчанию

NULL); // дополнительных атрибутов нет

// проверяем связь с каналом

if (hNamedPipe==INVALID_HANDLE_VALUE)

{

cerr << "Connection with the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// определяем состояние канала

if (!GetNamedPipeHandleState(

hNamedPipe, // дескриптор именованного канала

&dwState, // состояние именованного канала

&dwCurInstances, // количество экземпляров канала

&dwMaxCollectionCount, // размер буфера клиента канала

&dwCollectDataTimeout, // макс. задержка перед передачей данных

chUserName, // имя пользователя канала

255)) // максимальная длина имени

{

cerr << "Get named pipe handle state failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим интервал ожидания на консоль

cout << "Collection data timeout: " << dwCollectDataTimeout << endl;

// изменяем состояние именованного канала

dwCollectDataTimeout = 100;

if (!SetNamedPipeHandleState(

hNamedPipe, // дескриптор именованного канала

NULL, // режим передачи данных не изменяем

NULL, // размер буфера не изменяем

&dwCollectDataTimeout)) // макс. задержка равна 100 миллисекунд

{

cerr << "Set named pipe handle state failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

Page 36: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

36

}

// определяем состояние канала

GetNamedPipeHandleState(

hNamedPipe, // дескриптор именованного канала

&dwState, // состояние именованного канала

&dwCurInstances, // количество экземпляров канала

&dwMaxCollectionCount, // размер буфера клиента канала

&dwCollectDataTimeout, // макс. задержка перед передачей данных

chUserName, // имя пользователя канала

255); // максимальная длина имени

// выводим интервал ожидания на консоль

cout << "Collection data timeout: " << dwCollectDataTimeout << endl;

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

ЛЕКЦИЯ 6

Работа с именованными каналами

1. Получение информации об атрибутах именованного канала, которые не

могут быть изменены

Получение информации об именованном канале

Для получения информации об атрибутах именованного канала,

которые не могут быть изменены, используется функция GetNamedPipeInfo,

которая имеет следующий прототип:

BOOL GetNamedPipeInfo (

HANDLE hNamedPipe, // дескриптор именованного канала

LPDWORD lpFlags, // тип канала

LPDWORD lpOutBufferSize, // размер выходного буфера

LPDWORD lpInBufferSize, // размер входного буфера

LPDWORD lpCollectionDataTimeout, // интервал ожидания

LPTSTR lpUserName, // имя клиента именованного канала

Page 37: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

37

DWORD dwMaxUserNameSize // длина буфера для имени

клиента

В случае успешного завершения функция GetNamedPipeHandlestate воз-

вращает ненулевое значение, а в случае неудачи — FALSE.

Параметры функции имеют следующее назначение.

В параметре hNamedPipe должен быть установлен дескриптор

именованного канала. Причем канал должен быть открыт в режиме чтения.

Параметр lpstate должен указывать на переменную типа DWORD, в

которую функция записывает любую комбинацию следующих значений:

□ PIPE_NOWAIT — канал не блокирован;

□ PIPE_READMODE_MESSAGE — канал открыт в режиме передачи данных

сообщениями.

Если значение PIPE_NOWAIT не установлено, то канал блокирован. Если не

установлено значение PIPE_READMODE_MESSAGE, TO канал открыт в режиме

передачи данных потоком. Если определять состояние именованного каната не

нужно, то в параметре lpstate может быть установлено значение NULL

Параметр lpCurrentInstances должен указывать на переменную типа

DWORD, в которую функция записывает количество созданных экземпляров

именованного канала. Если эта информация не нужна, то значение этого

параметра может быть установлено в NULL.

Параметр lpMaxCollectionCount должен указывать на переменную типа

DWORD, в которую функция запишет максимальное количество байтов, которые

клиент именованного канала должен записать в этот канал, прежде чем данные

будут переданы серверу канала. Этот параметр должен быть установлен в NULL,

если функция вызывается сервером именованного канала или если клиент и

сервер работают на одном компьютере и для связи с сервером клиент использует

символ "." вместо имени сервера. Если информация о максимальном

количестве байтов не нужна, то в этом параметре может быть установлено

значение NULL.

Параметр lpCollectionDataTimeout должен указывать на переменную

DWORD, в которую функция поместит количество миллисекунд, которые могут

пройти, прежде чем данные могут быть переданы по сети. Как и в предыдущем

случае, этот параметр должен быть установлен в NULL в том случае, если

функция вызывается сервером именованного канала или клиент и сервер

работают на одном компьютере и для связи с сервером клиент использует

символ "." вместо имени сервера. Если информация об интервале задержки

Page 38: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

38

перед передачей данных не нужна, то в этом параметр может быть установлено

значение NULL.

Параметр lpUserName должен указывать на символьный массив, в котором

функция поместит строку с именем владельца именованного канала. Имя

владельца можно получить только в том случае, если доступ к каналу открыт

всем пользователям. Если информация об имени владельца канала не нужна,

то этот параметр может быть установлен в NULL.

Параметр dwMaxUserNamesize должен содержать размер области памяти,

на которую указывает параметр lpuserName.

В приложении 16-11 GetNamedPipeInfo приведена программа

процесса-клиента именованного канала, которая получает информацию о

неизменяемых атрибутах именованного канала посредством вызова функции

GetNamedPipeInfo.

ЛЕКЦИЯ 7

Использование почтовых ящиков в Windows

1. Функции почтовых ящиков

2. Реализация почтовых ящиков

3. Использование почтовых ящиков

1. Функции почтовых ящиков

Почтовые ящики, как и именованные каналы – это API, изначально

разработанные Microsoft для OS/2 LAN Manager и перенесенные в Windows

NT и более поздние версии ОС. Преимущество почтовых ящиков – в

поддержке широковещательной передачи. Оба API используют систему

защиты Windows, что позволяет серверам контролировать, какие клиенты

могут подключаться к ним.

Почтовые ящики предоставляют механизм ненадежного

одностороннего широковещания. Они обеспечивают только

однонаправленные соединения. Одним из примеров приложений,

использующих этот тип коммуникационной связи, является сервис

синхронизации времени, который каждые несколько секунд

широковещательно рассылает в пределах домена сообщение с эталонным

временем. Такие сообщения не критичны для работы компьютеров в сети,

поэтому они рассылаются через почтовые ящики.

Каждый процесс, который создает почтовый ящик, является сервером

почтового ящика; он создает почтовый ящик вызовом CreateMailslot.

Page 39: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

39

Входным параметром этой функции является имя в форме \\. \mailslot\

имя_почтового_ящика. Сервер может создавать ящик только на той машине,

на которой он работает, а назначаемые им имена почтовых ящиков могут

включать подкаталоги. CreateMailslot также принимает необязательный

дескриптор защиты, контролирующий доступ клиента к почтовому ящику.

Операции с ящиками являются асинхронными.

Поскольку почтовые ящики поддерживают одностороннюю

ненадежную передачу, число параметров CreateMailslot меньше, чем у

CreateNamedRipe. После создания ящика сервер просто отслеживает

поступающие клиентские сообщения, вызывая функцию ReadFile и указывая

дескриптор, представляющий почтовый ящик.

Другие процессы, посылающие сообщения серверу, являются его

клиентами. Они записывают сообщения в почтовый ящик. Входящие

сообщения всегда дописываются в почтовый ящик и сохраняются до тех

пор, пока сервер их не прочтет. Каждый процесс может быть одновременно и

сервером, и клиентом почтового ящика, обеспечивая таким образом

двунаправленное соединение. Клиенты почтового ящика используют формат

имени, аналогичный формату имени именованных каналов, за исключением

отличий, необходимых для широковещательной передачи сообщений всем

почтовым ящикам с данным именем в домене клиента или в другом

указанном домене. Чтобы послать сообщение в определенный экземпляр

почтового ящика, клиент вызывает функцию CreateFile, указывая имя

компьютера, на котором функционирует сервер; например,

\\Сервер\Mailslot\ИмяПочтовогоЯщика. Для представления локального

компьютера клиент задает \\.\ .

Клиент может посылать сообщения на почтовый ящик, расположенный

на том же компьютере, на компьютере в сети или на все почтовые ящики с

одним именем всем компьютерам выбранного домена. Если клиент хочет

получить описатель, представляющий все почтовые ящики с заданным

именем в своем домене, он указывает в вызове функции CreateFile имя

почтового ящика в следующем виде:

\\*\mailslot\ ИмяПочтовогоЯщика

При этом символ звездочки (*) действует в качестве группового

символа, и клиент может обнаружить любой сервер в пределах имени

домена - группы систем, объединенных общим именем, которое

назначается администратором сети.

Чтобы передать сообщение всем ящикам с указанным именем внутри

другого домена, следует использовать NETBIOS-имя домена (см.

приложение «NETBIOS имена») :

Page 40: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

40

\\ИмяДомена\mailslot\ [path] ИмяПочтовогоЯщика

Получив описатель, представляющий клиентскую сторону почтового

ящика, клиент посылает сообщения через функцию WriteFile. Реализация

почтовых ящиков допускает широковещательную передачу сообщений,

транслируемых по домену, длиной не более 425 байт. Если длина сообщений

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

требующей соединения клиента с сервером по типу «один-к-одному», что

исключает возможность широковещательной передачи. Кроме того, он урезает

425 байт до 424 байт. Таким образом, почтовые ящики непригодны для

рассылки сообщений, длина которых превышает 424 байта. В остальных

случаях размер сообщения ограничивается только при создании почтового

ящика сервером.

Клиенты и серверы, использующие почтовые ящики, при работе с ними

пользуются функциями, представленными в таблице:

Функции почтовых ящиков

CreateMailslot

Создает почтовый ящик и возвращает его дескриптор

GetMailslotlnfo Извлекает максимальный размер сообщения, размер почтового ящика, размер следующего сообщения в ящике, количество сообщений и время ожидания сообщения при выполнении операции чтения

SetMailslotlnfo Изменение таймаута при чтении из почтового ящика

DuplicateHandle Дублирование дескриптора почтового ящика

ReadFile, ReadFileEx Считывание сообщений из почтового ящика

GetFileTime

etFileTime

Получение даты и времени создания mailslot'a

SetFileTime Установка даты и времени создания mailslot'a

GetHandlelnformation Получение свойств дескриптора почтового ящика

SetHandlelnformation Установка свойств дескриптора почтового ящика

Page 41: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

41

Рассмотрим последовательно все операции, необходимые для корректной

работы с почтовыми ящиками.

1. Создание почтового ящика. Операция выполняется процессом сервера

с использованием функции CreateMailslot:

HANDLE CreateMailslot(

LPCTSTR IpName, // Имя почтового ящика.

DWORD nMaxMessageSize, //Максимальный размер сообщения.

DWORD IReadTimeout, // Таймаут (время ожидания) операции чтения.

LPSECURITY_ATTRIBUTES // Опции наследования и

IpSecurityAttributes // безопасности

);

2. Запись сообщений в почтовый ящик производится аналогично

записи в стандартный дисковый файл. Следующий код иллюстрирует, как с

помощью функций CreateFile, WriteFile и CloseHandle можно поместить

сообщение в почтовый ящик:

LPSTR IpszMessage= "Сообщение для sample_mailslot в текущем домене.";

BOOL fResult;

HANDLE hFile;

DWORD cb Written;

// С помощью функции CreateFile клиент открывает ящик для записи

сообщений

hFile = CreateFile ("\\\\\*\\mailslot\\sample_mailslot",

GENERIC_WR1TE,

FILE_SHARE_READ, // Требуется для записи в mailslot

(LPSECURITY_ATTRIBUTES) NULL,

OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL,

(HANDLE) NULL);

if (hFile == INVALID _HANDLE_VALUE)

{

ErrorHandler(hwnd, "Ошибка открытия почтового ящика");

return FALSE;

}

Page 42: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

42

//Запись сообщения в почтовый ящик

fResult =

WriteFile(hFile,

IpszMessage,

(DWORD) Istrlen(lpszMessage) + 1, //включая признак конца строки

&cb Written,

(LPOVERLAPPED) NULL);

if(!fResult)

{

ErrorHandler(hwnd, "Ошибка при записи сообщения ");

return FALSE;

}

TextOut(hdc, 10, 10, "Сообщение отправлено успешно.", 21);

fResult = CloseHandle(hFile);

if(!fResult)

{

ErrorHandler(hwnd, "Ошибка при закрытии дескриптора");

return FALSE;

}

TexlOut(hdc, 10, 30, "Дескриптор закрыт успешно.", 23);

return TRUE;

3. Чтение сообщений из почтового ящика. Создавший почтовый ящик

процесс получает право считывания сообщений из него, используя дескриптор

почтового ящика в вызове функции ReadFile.

Почтовый ящик существует до тех пор, пока не вызвана функция

CloseHandle на сервере или пока существует сам процесс сервера. В обоих

случаях все непрочитанные сообщения удаляются из почтового ящика,

уничтожаются все клиентские дескрипторы и почтовый ящик удаляется из

памяти.

4. Функция GetMailslollnfo считывает параметры почтового ящика:

BOOL GetMailslollnfo(

HANDLE hMailslot, // Дескриптор почтового ящика.

LPDWORD IpMaxMessageSize, // Максимальный размер сообщения

LPDWORD IpNextSize, //Размер следующего непрочитанного сообщения

LPDWORD IpMessageCount, // Количество сообщений.

LPDWQRD IpReadTimeout // Таймаут операции чтения.

Page 43: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

43

);

4. Функция SetMailslotlnfo устанавливает таймаут операции чтения:

BOOL SetMailslotlnfo(

HANDLE hMailslot, // Дескриптор почтового ящика.

DWORD IReadTimeout // Новый таймаут операции чтения.

);

1. Реализация почтовых ящиков

Почтовые ящики, как и именованные каналы, реализованы в Kernel32.dll.

Функции ReadFile и WriteFile, используемые приложениями для обмена

сообщениями через именованные каналы и почтовые ящики, являются

основными Windows–функциями ввода-вывода. CreateFile, с помощью

которой клиент открывает почтовый ящик или именованный канал, также

является стандартной Windows-функцией ввода-вывода. Имена,

указываемые приложениями при использовании почтовых ящиков, относятся

к пространству имен под управлением драйвера файловой системы почтовых

ящиков (\Windows\System32\Drivers\Msfs.sys). Драйвер файловой системы

почтовых ящиков создает объект «устройство» \Device\Mailslot и

символьную ссылку \Global??\Mailslot , которая указывает на этот объект.

Префикс \\.\ в именах ящиков, передаваемых CreateFile , транслируются в

\Global??\ , чтобы эти имена разрешались через символьную ссылку на

объект «устройство». Специальная функция CreateMailslot использует

соответствующую функцию ядра NtCreateMailslotFile. Когда почтовый ящик

создается сервером либо открывается клиентом, в конечном счете вызывается

соответствующий драйвер файловой системы на той машине, где находится

почтовый ящик. Такая реализация почтовых ящиков через драйвер

файловой системы ядра позволяет использовать объекты «файл» для их

представления и применять к ним файловые функции.

Отсюда следует, что почтовый ящик является псевдофайлом, находящимся

в памяти, и используются стандартные функции для работы с файлами, чтобы

получить доступ к нему. Данные в почтовом ящике могут быть в любой форме

– их интерпретацией занимается прикладная программа, но их общий объем не

должен превышать 64 Кбайт. Однако, в отличие от дисковых файлов, ящики

являются временными - когда все дескрипторы почтового ящика закрыты, он и

все его данные удаляются. Отметим, что все почтовые ящики являются

локальными по отношению к создавшему их процессу: процесс не может

создать удаленный ящик.

Page 44: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

44

3. Использование почтовых ящиков

Почтовые ящики предполагают несколько способов их использования.

Один из сценариев, в котором решается задача обнаружения сервера,

состоит в следующем.

Сервер приложения (действуя в качестве почтового клиента),

периодически осуществляет широковещательную рассылку своего имени и

имени именованного канала. Любой клиент приложения, которому требуется

найти сервер, может получить это имя, действуя в качестве сервера почтовых

ящиков. Аналогичным образом сервер командной строки может периодически

осуществлять широковещательную рассылку своего состояния, включая

информацию о коэффициенте использования, клиентам. Это соответствует

ситуации, когда имеется одна записывающая программа (почтовый клиент) и

несколько считывающих программ (почтовых серверов). Если бы почтовых

клиентов, то есть серверов приложения, было несколько, то ситуация бы

описывалась отношением типа «многие ко многим».

Возможен и другой вариант, когда одна считывающая программа получает

сообщения от многочисленных записывающих программ, которые, например,

предоставляют информацию о своем состоянии. Этот вариант,

соответствующий электронной доске объявлений, оправдывает термин

почтовый ящик.

Оба описанных варианта использования – широковещательная рассылка

имени и информации о состоянии – могут быть объединены, чтобы клиент мог

выбирать наиболее подходящий сервер. Обмен ролями терминов клиент и

сервер в данном контексте может сбивать с толку; однако следует заметить, что

сервер именованного канала и почтовый сервер (ящика) выполняют вызовы

функций CreateNamedPipe и CreateMailslot, тогда как клиент

(именованного канала или почтового ящика) создает соединение, используя

функцию CreateFile. Кроме того, в обоих случаях первый вызов функции

WriteFile выполняется клиентом, а первый вызов функции ReadFile

выполняется сервером.

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

будет использовать в операциях ReadFile, почтовые серверы (программы

считывания) вызывают функцию CreateMailslot. На одном компьютере может

находиться только один почтовый ящик с данным именем, но один и тот же

почтовый ящик может использоваться несколькими системами в сети, что

обеспечивает возможность работы с ним нескольким программам считывания.

Page 45: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

45

ЛЕКЦИЯ 8

Работа с почтовыми ящиками в Windows

1. Концепция почтовых ящиков в Windows

2. Создание почтовых ящиков

3. Соединение клиентов с почтовым ящиком

4. Обмен данными через почтовый ящик

1. Концепция почтовых ящиков

Почтовым ящиком называется объект ядра операционной системы, который

обеспечивает передачу сообщений от процессов-клиентов к процессам-

серверам, выполняющимся на компьютерах в пределах локальной сети.

Процесс, который создает почтовый ящик, называется сервером почтового

ящика. Процессы, которые связываются с именованным почтовым ящиком,

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

Характеристики почтовых ящиков:

- имеют имя, которое используется клиентами для связи с почтовыми

ящиками;

- направление передачи данных - от клиента к серверу;

- передача данных осуществляется сообщениями;

- обмен данными может быть как синхронным, так и асинхронным.

Передача данных осуществляется только от клиента к серверу.

Несколько серверов могут создать почтовые ящики с одинаковыми именами, и

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

получать все серверы этого почтового ящика. Однако в этом случае длина

сообщения должна быть меньше 425 байт, так как сообщения передаются

дейтаграммами.

Таким образом, можно сказать, что почтовые ящики обеспечивают

однонаправленную связь типа "многие-ко-многим". При этом доставка

сообщения от клиента к серверам почтового ящика не подтверждается системой.

Заметим также, что операционные системы семейства Windows NT не

поддерживают передачу сообщений длиной 425 и 426 байт.

Если длина сообщения меньше, чем 425 байт, то такое сообщение

передается как дейтаграмма. Дейтаграмма представляет собой небольшой

пакет с передаваемым по сети сообщением, который содержит также

информацию об отправителе и получателе сообщения. Дейтаграмма рассылается

всем серверам данного почтового ящика. Так как размер пакета небольшой, то

Page 46: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

46

дейтаграммы рассылаются быстро, но нет гарантии доставки сообщения, так как

в дейтаграмме не хранится информация, поддерживающая контроль доставки.

Если же длина сообщения больше 426 байт, то такие сообщения могут

передаваться только от одного клиента к одному серверу, используя при этом

SMB (Server Message Block)- протокол передачи данных по сети. При этом

отметим, что длина сообщения, передаваемого в почтовый ящик, не может

превышать 64 Кбайт.

Почтовые ящики можно рассматривать как псевдофайлы, расположенные

в оперативной памяти компьютера. Поэтому для доступа к почтовым ящикам

используются те же функции, что и для доступа к обычным файлам.

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

дальнейшем:

1. Создание почтового ящика сервером.

2. Соединение клиента с почтовым ящиком.

3. Обмен данными через почтовый ящик.

4. Закрытие почтового ящика клиентом и сервером.

2. Создание почтовых ящиков

Создаются почтовые ящики процессом-сервером при помощи функции

CreateMailslot, которая имеет следующий прототип:

HANDLE CreateMailslot(

LPCTSTR lpName, // имя почтового ящика

DWORD dwMaxMessageSize // максимальная длина сообщения

DWORD dwReadTimeout, // интервал ожидания

LPSECURITY_ATTRIBUTES IpSecurityAttributes // атрибуты безопасности

);

В случае успешного завершения эта функция вернет дескриптор

почтового ящика, а в случае неудачи — значение INVALID_HANDLE_VALUE.

Опишем параметры функции.

Параметр lpName указывает на строку, которая должна иметь вид:

\\. \mailslot\ mailslot_name

Здесь символ "." обозначает локальную машину, так как новый почтовый

ящик всегда создается на локальной машине, слово mailslot — фиксировано, a

mailslot_name обозначает имя почтового ящика, которое задается

пользователем и нечувствительно к верхнему и нижнему регистрам.

Параметр dwMaxMessageSize задает максимальную длину сообщения в

байтах, которое может быть записано в почтовый ящик.

Page 47: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

47

Параметр dwReadTimeout задает в миллисекундах временной интервал, в

течение которого функция ReadFile ждет поступления сообщения в почтовый

ящик. Если в этом параметре установлено значение 0, то в случае отсутствия в

почтовом ящике сообщения функция немедленно возвращает управление. Для

задания бесконечного времени ожидания в этом параметре нужно установить

значение MAILSLOT_WAIT_FOREVER.

Несколько процессов могут создать почтовые ящики с одним и тем же

именем. В этом случае сообщение, посланное клиентом, доставляется не только

в почтовый ящик одного процесса, а также в почтовые ящики всех таких

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

Режим доставки сообщений зависит от режима открытия почтового ящика

клиентом.

Пример создания почтового ящика сервером будет показан далее в разделе,

посвященном обмену данными через почтовый ящик.

3. Соединение клиентов с почтовым ящиком

Для установления связи с почтовым ящиком клиент использует функцию

CreateFile, которая имеет следующий прототип:

HANDLE CreateFile(

LPCTSTR lpFileName, // указатель на имя почтового ящика

DWORD dwDesiredAccess, // чтение или запись з канал

DWORD dwShareMode, // режим совместного использования

LPSECURITY_ATTRIBUTES IpSecurityAttributes, // атрибуты безопасности

DWORD dwCreationDisposition, // флаги открытия почтового ящика

DWORD dwFlagsAndAttributes, // флаги и атрибуты

HANDLE hTemplateFile // дополнительные атрибуты

);

В случае успешного завершения эта функция возвращает дескриптор почто-

вого ящика, а в случае неудачи — значение INVALID_HANDLE_VALUE.

Параметры функции CreateFile могут принимать следующие значения

(если эта функция используется для открытия почтового ящика:

Параметр lpFileName должен указывать на имя почтового ящика, которое

может быть задано в одном из следующих форматов:

- почтовый ящик на данном локальном компьютере:

\\ . \mailslot\имя_почтового_ящика

- почтовый ящик на компьютере с указанным именем:

\\имя_компьтера\ mailslot \имя_почтового_ящика

Page 48: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

48

- почтовый ящик в домене с указанным именем:

\\имя_домена\ mailslot \имя_почтового_ящика

- почтовый ящик в первичном (т.е. данном) домене системы:

\\*\ mailslot \имя_почтового_ящика

В первом случае сообщения будут доставляться только в почтовые ящики с

заданным именем, которые расположены на локальной машине. Во втором

случае сообщения будут доставляться в почтовые ящики с заданным именем,

расположенные на компьютере с указанным именем. В третьем случае

сообщения будут доставляться в почтовые ящики с заданным именем, которые

созданы внутри домена с указанным именем. В четвертом случае сообщения

будут доставляться в почтовые ящики с заданным именем, которые созданы

внутри первичного домена системы.

Параметр dwDesiredAccess должен иметь значение GENERIC_WRITE, которое

разрешает запись в почтовый ящик.

Параметр dwShareMode определяет режим совместного использования

почтового ящика и может принимать любую комбинацию из следующих

значений:

FILE_SHARE_READ — разрешает совместное чтение из почтового ящика;

FILE_SHARE_WRITE — разрешает совместную запись в почтовый ящик.

Параметр lpSecurityAttributes задает атрибуты безопасности почтового

ящика. Пока этот параметр будем устанавливать в NULL.

Для почтового ящика параметр dwCreationDisposition должен быть равен

значению OPEN_EXISTING, т.к. клиент всегда открывает существующий почтовый

ящик.

Для почтового ящика параметр dwFlagsAndAttributes можно задать

равным 0, что определяет флаги и атрибуты по умолчанию, или установить в этом

параметре значение FILE_ATTRIBUTE_NORMAL.

Параметр hTemplateFile при работе с почтовыми ящиками не

используется, поэтому в нем устанавливается значение NULL.

Пример соединения клиента с почтовым ящиком приводится в следующем

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

4. Обмен данными через почтовый ящик

Для обмена данными через почтовый ящик используются обычные функции

доступа к файлу WriteFile и ReadFile. Процесс-клиент записывает данные в

Page 49: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

49

почтовый ящик при помощи функций writeFile, а процесс-сервер читает данные

из почтового ящика, используя функции ReadFile.

Приведем программы, которые обмениваются данными через почтовый

ящик. Конечно, процесс-сервер также может записывать сообщения в почтовый

ящик, но это имеет смысл лишь в том случае, если в домене расположено

несколько почтовых ящиков с одинаковыми именами, которые были созданы

разными процессами. Тогда одинаковые сообщения получают все почтовые

ящики с одинаковыми именами.

Сначала, в листинге 1, приведем программу процесса-сервера, который

создает почтовый ящик. В этой программе почтовый ящик создается на ло-

кальной машине.

Листинг 1. Процесс-сервер почтового ящика – Приложение 17-01-

Server.cpp

#include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hMailslot; // дескриптор почтового ящика

// создаем почтовый ящик

hMailslot = CreateMailslot(

"\\\\.\\mailslot\\demo_mailslot", // имя почтового ящика

0, // длина сообщения произвольна

MAILSLOT_WAIT_FOREVER, // ждем сообщения произвольно долго

NULL // безопасность по умолчанию

);

// проверяем на успешное создание

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create mailslot failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

cout << "The mailslot is created." << endl;

cout << "The mailslot is waiting a message." << endl;

// читаем одно целое число из почтового ящика

int nData;

DWORD dwBytesRead;

if (!ReadFile(

Page 50: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

50

hMailslot, // дескриптор почтового ящика

&nData, // адрес буфера для ввода данных

sizeof(nData), // количество читаемых байтов

&dwBytesRead, // количество прочитанных байтов

(LPOVERLAPPED)NULL // передача данных синхронная

))

{

cerr << "Read file failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hMailslot);

cout << "Press any key to finish server.";

cin.get();

return 0;

}

// выводим число на консоль

cout << "The number " << nData << " was read by the server" << endl;

// закрываем дескриптор почтового ящика

CloseHandle(hMailslot);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

Теперь, в листинге 2, приведем программу процесса-клиента почтового ящика.

В этой программе предполагается, что процесс-клиент запускается в той же

локальной машине, что и процесс-сервер. Иначе в имени почтового ящика нужно

указать не точку, а имя компьютера, на котором создан почтовый ящик, или имя

домена, в котором созданы почтовые ящики с одинаковыми именами.

Листинг 2. Процесс-клиент почтового ящика - Приложение 17-02-

Client.cpp

#include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hMailslot;

char mailslotName[] = "\\\\.\\mailslot\\demo_mailslot";

// связываемся с почтовым ящиком

hMailslot = CreateFile(

mailslotName, // имя почтового ящика

GENERIC_WRITE, // записываем в ящик

FILE_SHARE_READ, // разрешаем одновременное чтение из ящика

NULL, // защита по умолчанию

OPEN_EXISTING, // открываем существующий канал

0, // атрибуты по умолчанию

Page 51: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

51

NULL // дополнительных атрибутов нет

);

// проверяем связь с почтовым ящиком

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create file failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish the client.";

cin.get();

return 0;

}

// вводим целое число

int n;

cout << "Input an integer: ";

cin >> n;

// пишем число в почтовый ящик

DWORD dwBytesWritten;

if (!WriteFile(

hMailslot, // дескриптор почтового ящика

&n, // данные

sizeof(n), // размер данных

&dwBytesWritten, // количество записанных байтов

NULL // синхронная запись

))

{

// ошибка записи

cerr << "Write file failed: " << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish the client.";

cin.get();

CloseHandle(hMailslot);

return 0;

}

// закрываем дескриптор канала

CloseHandle(hMailslot);

// завершаем процесс

cout << "The number is written by the client." << endl

<< "Press any key to exit." << endl;

cin.get();

return 0;

}

Page 52: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

52

ЛЕКЦИЯ 9

Работа с почтовыми ящиками в Windows 5. Получение информации о почтовом ящике

6. Изменение времени ожидания сообщения

1. Получение информации о почтовом ящике [1]

Для получения информации о характеристиках почтового ящика

используется функция GetMailslotInfo, которая имеет следующий прототип:

BOOL GetMailslotInfo (

HANDLE hMailslot, // дескриптор почтового ящика

LPDWORD IpMaxMessageSize // максимальная длина сообщения

LPDWORD lpNextSize, // длина следующего сообщения

LPDWORD lpMessageCount // количество сообщений

LPDWORD lpReadTimeout // интервал ожидания сообщения

);

В случае успешного завершения эта функция возвращает ненулевое значе-

ние, а в случае неудачи — NULL. Параметры функции GetMailslotInfo имеют

следующее назначение.

В параметре hMailslot должен быть установлен дескриптор почтового ящи-

ка, который был возвращен функцией CreateMailslot.

Параметр lpMaxMessageSize должен указывать на переменную типа

DWORD, в которую функция GetMailslotInfo поместит максимальную длину

сообщения, которое может быть записано в почтовый ящик. Если это значение

не нужно, то этот параметр может быть установлен в NULL.

Параметр lpNextSize должен указывать на переменную типа DWORD, в ко-

торую функция GetMailslotInfo поместит длину следующего сообщения в

почтовом ящике. Если в почтовом ящике нет сообщений, то в этот параметр

функция запишет значение MAILSLOT_NO_MESSAGE. Если значение длины

последнего сообщения не нужно, то параметр lpNextSize может быть установлен

в NULL.

Параметр lpMessageCount должен указывать на переменную типа

DWORD, .в которую функция GetMailslotInfo поместит количество сообщений,

находящихся в почтовом ящике. Если это значение не нужно, то этот параметр

может быть установлен в NULL.

Page 53: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

53

Параметр lpReadTimeout должен указывать на переменную типа DWORD,

в которую функция GetMailslotInfo поместит целое число без знака, обо-

значающее временной интервал в миллисекундах. Если почтовый ящик пуст, то

в течение этого интервала функция ReadFile будет ждать, пока процесс-клиент не

запишет сообщение в почтовый ящик. Если это значение не нужно, то в этом

параметре может быть установлено значение NULL.

В листингах 17.03, 17.04 приведены программы, в которых функция

GetMailslotInfo используется для получения информации о сообщениях,

хранящихся в почтовом ящике.

Сначала приведена программа сервера почтового ящика, который читает

сообщения по мере их поступления от клиента.

Листинг 17.03. Процесс-сервер почтового ящика, читающий

сообщения по мере их поступления от клиента – Приложение 17-03-

Server.cpp

#include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hMailslot; // дескриптор почтового ящика

DWORD dwNextMessageSize; // размер следующего сообщения

DWORD dwMessageCount; // количество сообщений

// создаем почтовый ящик

hMailslot = CreateMailslot(

"\\\\.\\mailslot\\demo_mailslot", // имя почтового ящика

0, // длина сообщения произвольна

MAILSLOT_WAIT_FOREVER, // ждем сообщения произвольно долго

NULL // безопасность по умолчанию

);

// проверяем на успешное создание

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create mailslot failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

Page 54: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

54

cout << "The mailslot is created." << endl;

// ждем сообщений

cout << "Press any key to read messages." << endl;

cin.get();

// получаем информацию о почтовом ящике

if (!GetMailslotInfo(

hMailslot, // дескриптор почтового ящика

NULL, // максимальный размер сообщения не нужен

&dwNextMessageSize, // размер следующего сообщения

&dwMessageCount, // количество сообщений

NULL)) // интервал ожидания не нужен

{

cerr << "Get mailslot info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

// читаем сообщения

while (dwMessageCount != 0)

{

DWORD dwBytesRead;

char* pchMessage;

// захватываем память для сообщения

pchMessage = (char*) new char[dwNextMessageSize];

// читаем одно сообщение

if (!ReadFile(

hMailslot, // дескриптор канала

pchMessage, // адрес буфера для ввода данных

dwNextMessageSize, // количество читаемых байтов

&dwBytesRead, // количество прочитанных байтов

NULL // передача данных синхронная

))

{

cerr << "Read file failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hMailslot);

cout << "Press any key to finish server.";

cin.get();

return 0;

}

// выводим сообщение на консоль

Page 55: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

55

cout << "The message << " << pchMessage << " >> was read" << endl;

// получаем информацию о следующем сообщении

if (!GetMailslotInfo(

hMailslot, // дескриптор почтового ящика

NULL, // максимальный размер сообщения не нужен

&dwNextMessageSize, // размер следующего сообщения

&dwMessageCount, // количество сообщений

NULL)) // интервал ожидания не нужен

{

cerr << "Get mailslot info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

// освобождаем память для сообщения

delete[] pchMessage;

}

// закрываем дескриптор почтового ящика

CloseHandle(hMailslot);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

В листинге 17.04 приведена программа клиента почтового ящика,

которая посылает сообщения процессу-серверу.

Листинг 17.04. Процесс - клиент почтового ящика – Приложения 17-

04-Client.cpp

#include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

Page 56: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

56

{

HANDLE hMailslot;

char mailslotName[] = "\\\\.\\mailslot\\demo_mailslot";

// связываемся с почтовым ящиком

hMailslot = CreateFile(

mailslotName, // имя почтового ящика

GENERIC_WRITE, // записываем в ящик

FILE_SHARE_READ, // разрешаем одновременное чтение из ящика

NULL, // защита по умолчанию

OPEN_EXISTING, // открываем существующий канал

0, // атрибуты по умолчанию

NULL // дополнительных атрибутов нет

);

// проверяем связь с почтовым ящиком

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create file failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish the client.";

cin.get();

return 0;

}

// вводим количество передаваемых сообщений

int n;

cout << "Input a number of messages: ";

cin >> n;

cin.get();

// пишем сообщения в почтовый ящик

for (int i = 0; i < n; ++i)

{

DWORD dwBytesWritten;

char pchMessage[256];

int nMessageSize;

cout << "Input message: ";

// читаем сообщение

cin.getline(pchMessage, 256);

// определем длину сообщения

nMessageSize = strlen(pchMessage) + 1;

// пишем сообщение

if (!WriteFile(

hMailslot, // дескриптор почтового ящика

Page 57: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

57

pchMessage, // данные

nMessageSize, // размер данных

&dwBytesWritten, // количество записанных байтов

NULL // синхронная запись

))

{

// ошибка записи

cerr << "Write file failed: " << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish the client.";

cin.get();

CloseHandle(hMailslot);

return 0;

}

}

// закрываем дескриптор канала

CloseHandle(hMailslot);

// завершаем процесс

cout << "The messages are written by the client." << endl

<< "Press any key to exit." << endl;

cin.get();

return 0;

}

2. Изменение времени ожидания сообщения

Для изменения времени ожидания сервером сообщения от клиента используется

функция SetMailslotInfo, которая имеет следующий прототип:

BOOL SetMailslotInfo (

HANDLE hMailslot, // дескриптор почтового ящика DWORD

dwReadTimeout // интервал ожидания сообщения

);

В случае успешного завершения эта функция возвратит ненулевое

значение, а в случае неудачи — NULL.

В параметре hMailslot должен быть установлен дескриптор почтового ящика,

который был получен вызовом функции CreateMailslot.

Параметр dwReadTimeout задает в миллисекундах новый временной

интервал, в течение которого функция ReadFile ждет поступления сообщения

в почтовый ящик. Если в этом параметре устанавливается значение 0, то в

Page 58: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

58

случае отсутствия в почтовом ящике сообщения функция немедленно возвращает

управление. Для задания бесконечного времени ожидания в этом параметре

нужно установить значение MAILSLOT_WAIT_FOREVER.

В листинге 17.05 приведена программа, в которой функция

SetMailslotInfo используется для установки нового временного интервала для

ожидания поступления сообщения в почтовый ящик.

Листинг 17.05. Изменение времени ожидания сообщения –

Приложение 17-05-SetMailslotInfo.cpp

#include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hMailslot; // дескриптор почтового ящика

DWORD dwReadTimeout; // интервал для ожидания сообщения

// создаем почтовый ящик

hMailslot = CreateMailslot(

"\\\\.\\mailslot\\demo_mailslot", // имя почтового ящика

0, // длина сообщения произвольна

0, // интервал ожидания равен нулю

NULL // защита по умолчанию

);

// проверяем на успешное создание

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create mailslot failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

cout << "The mailslot is created." << endl;

// получаем информацию о почтовом ящике

if (!GetMailslotInfo(

hMailslot, // дескриптор почтового ящика

NULL, // максимальный размер сообщения не нужен

NULL, // размер следующего сообщения не нужен

NULL, // количество сообщений не нужно

&dwReadTimeout)) // интервал ожидания сообщения

Page 59: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

59

{

cerr << "Get mailslot info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

cout << "Read timeout: " << dwReadTimeout << endl;

if (!SetMailslotInfo(

hMailslot, // дескриптор почтового ящика

3000)) // изменяем интервал ожидания

{

cerr << "Set mailslot info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

// получаем информацию о почтовом ящике

if (!GetMailslotInfo(

hMailslot, // дескриптор почтового ящика

NULL, // максимальный размер сообщения не нужен

NULL, // размер следующего сообщения не нужен

NULL, // количество сообщений не нужно

&dwReadTimeout)) // интервал ожидания сообщения

{

cerr << "Get mailslot info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

cout << "Read timeout: " << dwReadTimeout << endl;

// закрываем дескриптор почтового ящика

CloseHandle(hMailslot);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

Page 60: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

60

ЛЕКЦИЯ 10

РАБОТА С СОКЕТАМИ

Организация передачи данных в сетях с использованием

интерфейса Windows Sockets

1. Принципы организации сокетов и механизмы их применения при сетевом

программировании

2. Инициализация приложения и завершение его работы

3. Создание и инициализация сокета

1. Принципы организации сокетов и механизмы их применения при

сетевом программировании

Ранее были рассмотрены способы установления межпроцессного

взаимодействия в ЭВМ с использованием именованных каналов и почтовых

ящиков. Следующей задачей будет дальнейшее освоение основ

программирования для сетей TCP/IP с помощью программного интерфейса

Windows Sockets, который имеется в операционных системах Microsoft

Windows, начиная с Windows 3.11 и выше.

Технология сокетов (sockets ) лежит в основе современного сетевого

программирования. На ней базируются в настоящее время основные

операционные среды (Unix, Windows). Эта технология была разработана в

университете г. Беркли (США) для системы Unix, поэтому сокеты иногда

называют сокетами Беркли. На идеологии сокетов реализуется механизм

взаимодействия не только партнеров по телекоммуникациям, но и процессов

в ЭВМ вообще. В стандарте семиуровневой модели OSI сокеты лежат на

транспортном уровне. Сокет является пограничным понятием между

протоколами телекоммуникаций и операционной системой ЭВМ.

Интерфейс WinSock API предоставляет средства организации передачи

данных с использованием дейтаграмм и каналов связи между узлами сети.

Он позволяет передавать данные не только с использованием протокола

TCP/IP, но и других протоколов, например, IPX/SPX.

Как известно, в локальных и глобальных сетях существует два

принципиально разных способа передачи данных: без установления

соединения (дейтаграммный) и с установлением соединения (канала связи).

Page 61: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

61

Первый из них предполагает посылку пакетов данных от одного узла

другому (или сразу нескольким узлам) без получения подтверждения о

доставке и даже без гарантии того, что передаваемые пакеты будут получены

в правильной последовательности. Примером такого протокола может

служить протокол UDP, который используется в сетях TCP/IP, или протокол

IPX, который является базовым в сетях Novell NetWare. Основные

преимущества дейтаграммных протоколов заключаются в высоком

быстродействии и возможности широковещательной передачи данных, когда

один узел отправляет сообщения, а другие их получают, причем все

одновременно.

Второй способ передачи данных предполагает создание канала

передачи данных между двумя различными узлами сети. При этом канал

создается средствами дейтаграммных протоколов, однако доставка пакетов в

канале является гарантированной. Пакеты всегда доходят в правильном

порядке, хотя быстродействие получается в среднем ниже за счет посылки

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

служить протоколы TCP и SPX. Протокол NETBIOS допускает передачу

данных с использованием как дейтаграмм, так и каналов связи.

В рассматриваемом в данном случае механизме передачи данных с

использованием любого из перечисленных выше способов создается объект

типа сокет. По своему назначению сокет больше всего похож на

идентификатор файла, который нужен для выполнения над файлом операций

чтения или записи.

Прежде чем приложение, запущенное на узле сети, сможет выполнять

передачу или прием данных, оно должно создать сокет и

проинициализировать его, указав некоторые параметры.

Для сокета необходимо указать три параметра. Это IP адрес,

связанный с сокетом, номер порта, для которого будут выполняться

операции передачи данных, а также тип сокета.

Что касается последнего параметра (тип сокета), то существуют

сокеты двух типов. Первый тип предназначен для передачи данных в виде

дейтаграмм, второй - с использованием каналов связи.

Механизм применения сокетов

Все сетевые приложения построены на технологии клиент-сервер; это

значит, что в сети существует по крайней мере одно приложение, являющее

Page 62: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

62

сервером, типичная задача которого - это ожидание запроса на подключение

от приложений-клиентов, которых может быть теоретически сколько угодно,

и выполнение всевозможных процедур в ответ на запросы клиентов. Для

клиент-серверной технологии абсолютно неважно, где расположены клиент и

сервер - на одной машине или на разных. Конечно, для успешного

соединения клиента с сервером клиенту необходимо иметь минимальный

набор данных о расположении сервера - для сетей TCP/IP это IP-адрес

компьютера, где расположен сервер, и адрес порта, на котором сервер

ожидает запросы от клиентов.

Каждый из компьютеров в сети TCP/IP имеет свой уникальный IP-

адрес, который используется для обмена данными с другими компьютерами.

Каждый посылаемый пакет от одного компьютера другому имеет адрес

отправителя и получателя, что позволяет его однозначно идентифицировать.

Однако в случае, если на компьютере работает множество приложений,

одновременно использующих сеть, такого набора атрибутов явно

недостаточно.

Для разрешения неоднозначности, кроме адреса, каждое соединение (то

есть каждый процесс) на каждом конце имеет идентификатор под названием

"порт". Этот идентификатор представляет число от 0 до 65535. Таким

образом, пара адрес+порт определяет сокет-канал, по которому два

компьютера обмениваются данными друг с другом. Только одно

приложение на одном компьютере в одно и то же время может

использовать конкретный порт, однако для серверных частей возможно

создание нескольких сокетов на одном порту для работы с несколькими

клиентами.

Для пользовательских программ обычно используются номера

портов в диапазоне 1025 – 5000. Порты с меньшими номерами

зарезервированы для таких известных служб, как telnet или ftp, а с

большими предполагаются для использования другими стандартными

службами.

Значение порта не обязательно должно совпадать на сервере и клиенте -

клиенту для соединения важно только знать порт сервера, порт клиента

может выбираться клиентом произвольно и становится известен серверу в

момент запроса клиента на соединение. Когда соединение будет установлено,

ОС создаст для серверного приложения соответствующий сокет, с которым и

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

важен.

Page 63: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

63

Механизм работы сокетов таков: на серверной стороне запускается

серверный сокет, который после запуска сразу переходит в режим

прослушивания (т.е. ожидания соединения клиентов). На стороне клиента

создается сокет, для которого указывается IP-адрес и порт сервера и дается

команда на соединение. Когда сервер получает запрос на соединение, ОС

создает новый экземпляр сокета, с помощью которого сервер может

обмениваться данными с клиентом. При этом сокет, который создан для

прослушивания, продолжает находиться в режиме приема соединений; таким

образом, программист может создать сервер, работающий с несколькими

подключениями от клиентов.

Работа с сокетами, по существу, это операции ввода-вывода, которые

бывают синхронные и асинхронные. В терминологии сокетов работа в

асинхронном режиме называется блокирующими сокетами, а в синхронном -

неблокирующие сокеты. Попытка соединения или приема данных в

блокирующем режиме (отправка всегда синхронна, так как фактически

является постановкой в очередь) означает, что пока программа не

соединится или не примет данные, передачи управления на следующий

оператор не произойдет.

Рассмотрим минимальный набор функций из WinSock API,

необходимых для написания элементарного клиента и сервера. Сами

функции находятся в файле winsock32.dll.

WSAStartup - функция сообщает ОС, что в любом процессе

приложения могут быть использованы функции WinSock. Функция должна

быть вызвана один раз при запуске приложения перед использованием

любой функции WinSock.

WSACleanup - функция сообщает ОС, что приложение более не

использует WinSock. Должна быть вызвана перед завершением приложения.

Socket - функция создает сокет. (Порт и адрес задается в функции

bind (сервер) или connect (клиент) – см. далее). Входящие параметры: af -

спецификация семейства сокетов (AF_INET, AF_IPX и др.), Struct -

спецификация типа нового сокета (принимает значение SOCK_STREAM или

SOCK_DGRAM), protocol - специфический протокол, который будет

использоваться сокетом (число). Если функция выполнена без ошибок, она

Page 64: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

64

возвращает дескриптор на новый сокет, если ошибки есть, возвращается

INVALID_SOCKET.

Bind - функция ассоциирует адрес с сокетом. Структура адреса

содержит порт (необходимо привести функцией htons) и адрес (для сервера

обычно указывается INADDR_ANY - любой).

Connect - функция соединения для клиента. Структура адреса

содержит порт (необходимо привести функцией htons) и адрес (для клиента

необходимо привести из имени или спецификации ip4 - xxx.xxx.xxx.xxx).

Send - функция отправки данных. Помещает в очередь сокета s кусок

данных из buf, длиной len. Последний параметр отвечает за вид передачи

сообщения. Может быть проигнорирован (0).

Recv - функция получения данных.

Итак, работа с сокетами содержит ряд этапов: сокет создается,

настраивается на заданный режим работы, применяется для организации

обмена и, наконец, ликвидируется.

Для этого необходимо:

- инициализировать приложение для работы с интерфейсом Windows

Sockets;

- создать сокет и убедиться, что он возвращает ненулевую величину,

которая является дескриптором сокета;

- заполнить структуру SOCKADDR_IN необходимыми данными,

включающими формат адреса, порт и IP-адрес;

- использовать bind() для привязки к определенному IP – адресу

(если использовать inet_addr("0.0.0.0") или htonl(INADDR_ANY) в секции

sin_addr структуры SOCKADDR_IN, можно привязать сокет к любому

адресу).

Затем можно предпринимать действия по созданию связи процессов с

помощью сокета.

Page 65: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

65

2. Инициализация приложения и завершение его работы

2.1. Прежде всего в процессе инициализации приложение должно

зарегистрировать себя в библиотеке WSOCK32.DLL, которая предоставляет

приложениям интерфейс WinSock API в среде операционных систем

Microsoft Windows. Для этого следует подключить к программе библиотеку

Ws2_32.lib:

#include <winsock2.h>

#pragma comment(lib,"Ws2_32.lib")

2.2. Эту DLL следует инициализировать с помощью нестандартной,

специфической для WinSock функции WSAStartup, которая должна быть

первой из функций WinSock, вызываемых программой. WSAStartup

определяется следующим образом:

int WSAStartup (WORD wVersionRequested, LPWSADATA lpWSAData);

В параметре wVersionRequested необходимо указать версию интерфейса

Windows Sockets, необходимую для работы приложения. Старший байт

параметра указывает младший номер версии (minor version), младший байт -

старший номер версии (major version).

Перед вызовом функции WSAStartup параметр lpWSAData должен

содержать указатель на структуру типа WSADATA , в которую будут

записаны сведения о конкретной реализации интерфейса Windows

Sockets.

В случае успеха функция WSAStartup возвращает нулевое значение.

Если происходит ошибка, возвращается одно из следующих значений:

Значение Описание

WSASYSNOTREADY Сетевое программное обеспечение не

готово для работы

WSAVERNOTSUPPORTED Функция не поддерживается данной

реализацией интерфейса Windows Sockets

WSAEINVAL Библиотека DLL, обеспечивающая

интерфейсe Windows Sockets, не соответствует версии, указанной в

параметре wVersionRequested

Page 66: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

66

Ниже представлен фрагмент исходного текста приложения SERVER,

выполняющий инициализацию интерфейса Windows Sockets:

WSAStartup (MAKEWORD(2, 2), &WSAData);

В операционных системах Microsoft Windows версий до Windows NT

включительно использовалась система Windows Sockets версии 1.1. В

настоящее время следует указывать версию 2.2:

WSAData wsd;

WSAStartup(0x0202,&wsd);

Определение структуры WSADATA и указателя на нее выглядят

следующим образом:

typedef struct WSAData

{

WORD wVersion;

WORD wHighVersion;

char szDescription[WSADESCRIPTION_LEN+1];

char szSystemStatus[WSASYS_STATUS_LEN+1];

unsigned short iMaxSockets;

unsigned short iMaxUdpDg;

char FAR * lpVendorInfo;

} WSADATA ;

typedef WSADATA FAR *LPWSADATA;

Использованные выше поля szDescription и szSystemStatus после вызова

функции WSAStartup содержат, соответственно, описание конкретной

реализации интерфейса Windows Socket и текущее состояние этого

интерфейса в виде текстовых строк.

В полях wVersion и wHighVersion записаны, соответственно, версия

спецификации Windows Socket, которую будет использовать приложение, и

версия спецификации, которой соответствует конкретная реализация

интерфейса Windows Socket.

Приложение может одновременно создавать несколько сокетов,

например, для использования в разных подзадачах одного процесса. В поле

iMaxSockets хранится максимальное количество сокетов, которое можно

получить для одного процесса.

Page 67: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

67

В поле iMaxUdpDg записан максимальный размер пакета данных,

который можно переслать с использованием дейтаграммного протокола UDP.

И, наконец, поле lpVendorInfo содержит указатель на дополнительную

информацию, формат которой зависит от фирмы-изготовителя конкретной

реализации системы Windows Sockets.

2.3. Перед тем, как завершить свою работу, приложение должно

освободить ресурсы, полученные у операционной системы для работы с

Windows Sockets. Для выполнения этой задачи приложение должно вызвать

функцию WSACleanup , определенную так, как это показано ниже:

int WSACleanup (void);

Эта функция может возвратить нулевое значение при успехе или

значение SOCKET_ERROR в случае ошибки.

Для получения кода ошибки необходимо воспользоваться функцией с

именем WSAGetLastError:

int WSAGetLastError (void);

Функция WSAGetLastError позволяет определить код ошибки при

неудачном завершении практически всех функций интерфейса Windows

Sockets.

Если ошибка возникла при выполнении функции WSACleanup,

функция WSAGetLastError может вернуть одно из следующих значений:

Значение Описание

WSANOTINITIALISED Интерфейс Windows Sockets не был

проинициализирован функцией WSAStartup

WSAENETDOWN Сбой сетевого программного обеспечения

WSAEINPROGRESS Во время вызова функции WSACleanup

выполнялась одна из блокирующих функций интерфейса Windows Sockets

Сделаем небольшие пояснения относительно последней ошибки,

приведенной в этом списке, имеющей код WSAEINPROGRESS .

Некоторые функции интерфейса Windows Sockets способны

блокировать работу приложения, так как они не возвращают управление до

своего завершения. В операционных системах, использующих вытесняющую

Page 68: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

68

многозадачность, к которым относятся ОС Microsoft Windows, это не

приводит к блокировке всей системы. Можно избежать использования

блокирующих функций, так как для них в интерфейсе Windows Sockets

существует замена.

3. Создание и инициализация сокета

После инициализации интерфейса Windows Sockets приложение

должно создать один или несколько сокетов, которые будут использованы

для передачи данных.

Создание сокета

Сокет создается с помощью функции socket , имеющей следующий

прототип:

SOCKET socket (int af, int type, int protocol);

Параметр af определяет формат адреса. Для этого параметра вы должны

указывать значение AF_INET , что соответствует формату адреса, принятому

в Internet.

Параметры type и protocol определяют, сооветственно, тип сокета и

протокол, который будет использован для данного сокета.

Можно указывать сокеты следующих двух типов:

Тип сокета Описание

SOCK_STREAM Сокет будет использован для передачи

данных через канал связи с использованием протокола TCP

SOCK_DGRAM Передача данных будет выполняться без

создания каналов связи через дейтаграммный протокол UDP

Что же касается параметра protocol, то можно указать для него

нулевое значение.

В случае успеха функция socket возвращает дескриптор, который нужно

использовать для выполнения всех операций над данным сокетом. Если же

произошла ошибка, эта функция возвращает значение INVALID_SOCKET .

Для анализа причины ошибки вы должны вызвать функцию

Page 69: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

69

WSAGetLastError , которая в данном случае может вернуть один из

следующих кодов ошибки:

Код ошибки Описание

WSANOTINITIALISED Интерфейс Windows Sockets не был

проинициализирован функцией WSAStartup

WSAENETDOWN Сбой сетевого программного обеспечения

WSAEAFNOSUPPORT Указан неправильный тип адреса

WSAEINPROGRESS Выполняется блокирующая функция

интерфейса Windows Sockets

WSAEMFILE Израсходован весь запас свободных дескрипторов

WSAENOBUFS Нет памяти для создания буфера

WSAEPROTONOSUPPORT Указан неправильный протокол

WSAEPROTOTYPE Указанный протокол несовместим с данным

типом сокета

WSAESOCKTNOSUPPORT Указанный тип сокета несовместим с

данным типом адреса

Ниже приведен фрагмент кода, в котором создается сокет для передачи

данных с использованием протокола TCP:

srv_socket = socket(AF_INET , SOCK_STREAM, 0);

Удаление сокета

Для освобождения ресурсов приложение должно закрывать сокеты,

которые ему больше не нужны, вызывая функцию closesocket :

int closesocket (SOCKET sock);

Ниже перечислены коды ошибок для этой функции:

Код ошибки Описание

WSANOTINITIALISED Перед использованием функции

closesocket необходимо вызвать функцию WSAStartup

WSAENETDOWN Сбой в сети

WSAENOTSOCK Указанный в параметре дескриптор не является

сокетом

WSAEINPROGRESS Выполняется блокирующая функция

интерфейса Windows Sockets

Page 70: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

70

WSAEINTR Работа функции была отменена при помощи

функции WSACancelBlockingCall

Параметры сокета

Перед использованием необходимо задать параметры сокета.

Для этого нужно подготовить структуру типа sockaddr , определение

которой показано ниже:

struct sockaddr

{

u_short sa_family;

char sa_data[14];

};

typedef struct sockaddr SOCKADDR ;

typedef struct sockaddr *PSOCKADDR ;

typedef struct sockaddr FAR *LPSOCKADDR ;

Для работы с адресами в формате Internet используется другой вариант

этой структуры, в котором детализируется формат поля sa_data:

struct sockaddr_in

{

short sin_family;

u_short sin_port;

struct in_addr sin_addr;

char sin_zero[8];

};

typedef struct sockaddr_in SOCKADDR _IN;

typedef struct sockaddr_in *PSOCKADDR _IN;

typedef struct sockaddr_in FAR *LPSOCKADDR _IN;

Поле sin_family определяет тип адреса. В это поле записывается

значение AF_INET , которое соответствует типу адреса, принятому в

Internet:

srv_address.sin_family = AF_INET ;

Поле sin_port определяет номер порта, который будет использоваться

для передачи данных.

Page 71: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

71

Порт - это просто идентификатор программы, выполняющей обмен на

сети. На одном узле может одновременно работать несколько программ,

использующих разные порты.

Особенностью поля sin_port является использование так называемого

сетевого формата данных. Этот формат требует, чтобы младшие байты

данных хранились по старшим адресам памяти. Универсальный сетевой

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

такой сети могут использоваться компьютеры с различной архитектурой.

Для выполнения преобразований из обычного формата в сетевой и

обратно в интерфейсе Windows Sockets предусмотрен специальный набор

функций. В частности, для заполнения поля sin_port нужно использовать

функцию htons, выполняющую преобразование 16-разрядных данных из

формата Intel в сетевой формат.

Ниже показано, как инициализируется поле sin_port в приложении

SERVER, описанном далее:

#define SERV_PORT 5000

srv_address.sin_port = htons(SERV_PORT);

Вернемся снова к структуре sockaddr_in .

Поле sin_addr этой структуры представляет собой структуру in_addr:

struct in_addr

{

union

{

struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;

struct { u_short s_w1,s_w2; } S_un_w;

u_long S_addr;

} S_un;

};

#define s_addr S_un.S_addr

#define s_host S_un.S_un_b.s_b2

#define s_net S_un.S_un_b.s_b1

#define s_imp S_un.S_un_w.s_w2

#define s_impno S_un.S_un_b.s_b4

#define s_lh S_un.S_un_b.s_b3

Page 72: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

72

При инициализации сокета в этой структуре вы должны указать адрес

IP, с которым будет работать данный сокет.

Если сокет будет работать с любым адресом (например, вы создаете

сервер, который будет доступен из узлов с любым адресом), адрес для

сокета можно указать следующим образом:

srv_address.sin_addr .s_addr = INADDR_ANY ;

В том случае, если сокет будет работать с определенным адресом IP

(например, вы создаете приложение-клиент, которое будет обращаться к

серверу с конкретным адресом IP), в указанную структуру необходимо

записать реальный адрес.

Дейтаграммный протокол UDP позволяет посылать пакеты данных

одновременно всем рабочим станциям в широковещательном режиме. Для

этого нужно указать адрес как INADDR_BROADCAST.

Если вам известен адрес в виде четырех десятичных чисел, разделенных

точкой (именно так его вводит пользователь), то вы можете заполнить поле

адреса при помощи функции inet_addr :

dest_sin.sin_addr .s_addr = inet_addr ("200.200.200.201");

В случае ошибки функция возвращает значение INADDR_NONE , что

можно использовать для проверки.

Обратное преобразование адреса IP в текстовую строку можно при

необходимости легко выполнить с помощью функции inet_ntoa , имеющей

следующий прототип:

char FAR * inet_ntoa (struct in_addr in);

При ошибке эта функция возвращает значение NULL.

Однако чаще всего пользователь работает с доменными именами,

используя сервер DNS или файл HOSTS . В этом случае вначале нужно

воспользоваться функцией gethostbyname , возвращающей адрес IP, а затем

записать полученный адрес в структуру sin_addr :

PHOSTENT phe;

phe = gethostbyname ("ftp.microsoft.com");

if(phe == NULL)

{

closesocket (srv_socket);

MessageBox(NULL, "gethostbyname Error", "Error", MB_OK);

return;

Page 73: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

73

}

memcpy((char FAR *)&(dest_sin.sin_addr ),

phe->h_addr , phe->h_length);

В случае ошибки функция gethostbyname возвращает NULL. При этом

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

WSAGetLastError .

Если же указанный узел найден в базе DNS или в файле HOSTS,

функция gethostbyname возвращает указатель на структуру hostent,

описанную ниже:

struct hostent

{

char FAR * h_name; // имя узла

char FAR * FAR * h_aliases; // список альтернативных имен

short h_addr type; // тип адреса узла

short h_length; // длина адреса

char FAR * FAR * h_addr _list; // список адресов

#define h_addr h_addr_list[0] // адрес

};

typedef struct hostent *PHOSTENT ;

typedef struct hostent FAR *LPHOSTENT ;

Искомый адрес находится в первом элемента списка h_addr _list[0], на

который можно также ссылаться при помощи h_addr. Длина поля адреса

находится в поле h_length.

Привязка адреса к сокету

После того как подготовлена структура SOCKADDR и в нее записаны

параметры сокета (в частности, адрес), следует выполнить привязку адреса к

сокету при помощи функции bind :

int bind (

SOCKET sock, const struct sockaddr FAR * addr, int namelen);

Параметр sock должен содержать дескриптор сокета, созданного

функцией socket .

В поле addr следует записать указатель на подготовленную структуру

SOCKADDR , а в поле namelen - размер этой структуры.

Page 74: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

74

В случае ошибки функция bind возвращает значение SOCKET_ERROR .

Дальнейший анализ причин ошибки следует выполнять при помощи

функции WSAGetLastError . Возможные коды ошибок перечислены ниже:

Код ошибки Описание

WSANOTINITIALISED Перед использованием функции необходимо

вызвать функцию WSAStartup

WSAENETDOWN Сбой в сети

WSAEADDRINUSE Указанный адрес уже используется

WSAEFAULT Значение параметра namelen меньше размера

структуры sockaddr

WSAEINPROGRESS Выполняется блокирующая функция

интерфейса Windows Sockets

WSAEAFNOSUPPORT Этот протокол не может работать с указанным

семейством адресов

WSAEINVAL Сокет уже привязан к адресу

WSAENOBUFS Установлено слишком много соединений

WSAENOTSOCK Указанный в параметре дескриптор не является

сокетом

Пример вызова функции bind показан ниже:

if(bind (srv_socket , (LPSOCKADDR )&srv_address,

sizeof(srv_address)) == SOCKET_ERROR )

{

closesocket (srv_socket);

MessageBox(NULL, "bind Error", "Error", MB_OK);

return;

}

Page 75: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

75

ЛЕКЦИЯ 11

РАБОТА С СОКЕТАМИ

Организация передачи данных в сетях с использованием

интерфейса Windows Sockets 1. Создание канала связи

2. Передача и прием данных

Как упоминалось ранее, работа с сокетами содержит ряд этапов: сокет

создается, настраивается на заданный режим работы, применяется для

организации обмена и, наконец, ликвидируется.

Для этого необходимо:

- инициализировать приложение для работы с интерфейсом Windows

Sockets;

- создать сокет и убедиться, что он возвращает ненулевую величину,

которая является дескриптором сокета;

- заполнить структуру SOCKADDR_IN необходимыми данными,

включающими формат адреса, порт и IP-адрес;

- использовать bind() для привязки к определенному IP – адресу

(если использовать inet_addr("0.0.0.0") или htonl(INADDR_ANY) в секции

sin_addr структуры SOCKADDR_IN, можно привязать сокет к любому

адресу).

Теперь можно предпринимать действия по созданию связи процессов с

помощью сокета.

1. Создание канала связи

Для передачи дейтаграммных сообщений при помощи протокола

негарантированной доставки UDP канал связи не нужен. Сразу после создания

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

передачи данных с использованием протокола TCP необходимо создать канал

связи.

Сторона сервера

Рассмотрим процедуру создания канала связи со стороны сервера.

Page 76: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

76

1). Прежде всего необходимо переключить сокет в состояние

прослушивания, то есть в режим ожидания соединения с клиентом, при

помощи функции listen, которая организует очередь запросов:

int listen(SOCKET sock, int backlog);

Функция listen подготавливает сокет к обработке потока запросов.

Через параметр sock функции необходимо передать дескриптор сокета,

который будет использован для создания канала связи. Параметр backlog

задает максимальный размер очереди для ожидания соединения (то есть,

сколько запросов может быть принято на обслуживание без потерь; обычно

можно указывать значения от 1 до 5, если используется версия WinSock 1.1; в

версии 2.0 и выше нет ограничений сверху). При переполнении очереди будет

послано сообщение об ошибке. Очередь содержит запросы на установку

соединений для каждой пары значений (адрес IP, порт). Ожидающий сокет

посылает каждому отправителю сообщение-отклик, подтверждающее

получение запроса на соединение. Следует иметь в виду, что клиент,

ориентированный на соединение, также должен прослушивать порт протокола,

ожидая появления дейтограмм-откликов.

Ниже приведен список возможных кодов ошибок для функции listen.

Код ошибки Описание

WSANOTINITIALISED Перед использованием функции необходимо вызвать

функцию WSAStartup

WSAENETDOWN Сбой в сети

WSAEADDRINUSE Указанный адрес уже используется

WSAEINPROGRESS Выполняется блокирующая функция интерфейса

Windows Sockets

WSAEINVAL Сокет еще не был привязан к адресу или уже находится в

подключенном состоянии

WSAEISCONN Сокет уже находится в подключенном состоянии

WSAEMFILE Недостаточно дескрипторов файлов

WSAENOBUFS Нет места для размещения буфера

WSAENOTSOCK Указанный в параметре дескриптор не является сокетом

WSAEOPNOTSUPP Функция listen не работает с сокетом указанного типа

Page 77: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

77

Пример вызова функции listen:

if(listen(srv_socket , 1) == SOCKET_ERROR )

{

closesocket (srv_socket);

MessageBox(NULL, "listen Error", "Error", MB_OK);

return;

}

2). Далее необходимо выполнить ожидание соединения. Это можно

выполнить двумя различными способами.

Первый способ заключается в циклическом вызове функции accept

до тех пор, пока не будет установлено соединение. Затем можно будет

приступать к обмену данными. Оператор accept извлекает запросы на

соединение из очереди.

Функция accept имеет следующий прототип:

SOCKET accept (SOCKET sock, struct sockaddr FAR * addr, int

FAR * addrlen);

Через параметр sock указывается дескриптор сокета, который

прослушивает соединение (тот же, что и в listen); addr — указатель на

структуру, которая содержит адрес буфера, в который будет записан адрес

узла, подключившегося к серверу; addrlen — код длины адреса.

Оператор accept позволяет серверу принять запрос от клиента. Когда

входная очередь сформирована, программа реализует процедуру accept и

переходит в режим ожидания запросов. Программа извлекает первый

элемент очереди, создает новый сокет со свойствами, идентичными sock, и

при успешном выполнении возвращает дескриптор нового сокета. При

возникновении ошибки возвращается код INVALID_SOCKET. По окончании

обработки запроса сервер вновь вызывает accept, который возвращает ему

дескриптор сокета очередного запроса, если таковой имеется. Если очередь

пуста, accept блокирует программу до получения связи.

Существуют серверы с параллельной и последовательной обработкой

запросов. Параллельный обработчик запросов не ждет завершения обработки

Page 78: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

78

предшествующего запроса и вызывает оператор accept немедленно. В

системе Unix используются обычно параллельные обработчики запросов.

Если ожидание соединения в цикле нежелательно, можно предложить

другой способ, основанный на использовании расширения программного

интерфейса Windows Socket, предназначенного для выполнения асинхронных

операций.

Приведем список возможных кодов ошибок для функции accept.

Код ошибки Описание

WSANOTINITIALISED Перед использованием функции необходимо вызвать

функцию WSAStartup

WSAENETDOWN Сбой в сети

WSAEFAULT Значение параметра addrlen меньше размера структуры

адреса

WSAEINTR Работа функции была отменена при помощи функции

WSACancelBlockingCall

WSAEINPROGRESS Выполняется блокирующая функция интерфейса

Windows Sockets

WSAEINVAL Перед вызовом функции accept не была вызывана

функция listen

WSAEMFILE Нет доступных дескрипторов

WSAENOBUFS Установлено слишком много соединений

WSAENOTSOCK Указанный в параметре дескриптор не является сокетом

WSAEOPNOTSUPP Данный тип сокета нельзя использовать при вызове

функций, ориентированных на работу с каналом связи

WSAEWOULDBLOCK Сокет отмечен как неблокирующий и в настоящее

время нет каналов связи, которые нужно устанавливать

Вместо того чтобы ожидать соединение, вызывая в цикле функцию

accept , приложение может вызвать один раз функцию WSAAsyncSelect ,

указав ей, что при получении запроса на установку соединения функция окна

вашего приложения должна получить сообщение:

#define WSA_ACCEPT (WM_USER + 1)

// При попытке установки соединения главное окно приложения получит сообщение

//WSA_ACCEPT

rc = WSAAsyncSelect (srv_socket , hWnd, WSA_ACCEPT, FD_ACCEPT );

if(rc > 0)

Page 79: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

79

{

closesocket (srv_socket);

MessageBox(NULL, "WSAAsyncSelect Error", "Error", MB_OK);

return;

}

В данном случае ожидание соединения выполняется для сокета

srv_socket . Последний параметр функции имеет значение FD_ACCEPT . Это

означает, что при попытке создания канала связи функция окна с

идентификатором hWnd получит сообщение WSA_ACCEPT, определенное в

приложении.

Обработчик этого сообщения может выглядеть, например, следующим

образом:

void WndProc_OnWSAAccept(HWND hWnd, UINT msg,

WPARAM wParam, LPARAM lParam)

{

int rc;

// При ошибке отменяем поступление извещений в главное окно приложения

if(WSAGETSELECTERROR(lParam) != 0)

{

MessageBox(NULL, "accept Error", "Error", MB_OK);

WSAAsyncSelect (srv_socket , hWnd, 0, 0);

return;

}

// Определяем размер адреса сокета

acc_sin_len = sizeof(acc_sin);

// Разрешаем установку соединения

srv_socket = accept (srv_socket, (LPSOCKADDR )&acc_sin,

(int FAR *)&acc_sin_len);

if(srv_socket == INVALID_SOCKET)

{

Page 80: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

80

MessageBox(NULL, "accept Error, invalid socket ",

"Error", MB_OK);

return;

}

// Если на данном сокете начнется передача данных от клиента, в главное окно

приложения //поступит сообщение WSA_NETEVENT. Это же сообщение поступит при

разрыве соединения

rc = WSAAsyncSelect (srv_socket , hWnd, WSA_NETEVENT,

FD_READ | FD_CLOSE );

if(rc > 0)

{

closesocket (srv_socket);

MessageBox(NULL, "WSAAsyncSelect Error", "Error", MB_OK);

return;

}

}

В данном случае обработчик сообщения вначале вызывает функцию

accept , выполняющую создание канала передачи данных. После этого

функция WSAAsyncSelect вызывается еще один раз для того, чтобы

установить асинхронную обработку приема данных от удаленного клиента, а

также обработку ситуации разрыва канала связи.

Сторона клиента

Рассмотрим процедуру установки канала связи со стороны клиента,

используемую в приложении CLIENT.

Для установки соединения в приложении используется функция сonnect:

SOCKADDR _IN dest_sin;

void SetConnection(HWND hWnd)

{

PHOSTENT phe;

// Создаем сокет

srv_socket = socket(AF_INET , SOCK_STREAM, 0);

if(srv_socket == INVALID_SOCKET)

Page 81: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

81

{

MessageBox(NULL, "socket Error", "Error", MB_OK);

return;

}

// Устанавливаем адрес IP и номер порта

dest_sin.sin_family = AF_INET ;

// Определяем адрес узла

phe = gethostbyname ("localhost ");

if(phe == NULL)

{

closesocket (srv_socket);

MessageBox(NULL, "gethostbyname Error", "Error", MB_OK);

return;

}

// Копируем адрес узла

memcpy((char FAR *)&(dest_sin.sin_addr ), phe->h_addr ,

phe->h_length);

// Копируем номер порта

dest_sin.sin_port = htons(SERV_PORT);

// Устанавливаем соединение

if(connect(srv_socket , (PSOCKADDR )&dest_sin,

sizeof(dest_sin)) < 0)

{

closesocket (srv_socket);

MessageBox(NULL, "connect Error", "Error", MB_OK);

return;

}

}

Page 82: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

82

Вначале с помощью функции socket функция SetConnection создает

сокет. Затем выполняется заполнение адресной информацией структуры

dest_sin.

Для получения адреса IP в данном примере используется функция

gethostbyname, для которой указано имя узла localhost . Это имя

отображается в файле HOSTS на адрес 127.0.0.1 :

1. localhost

Адрес 127.0.0.1 является локальным. Его можно использовать для

тестирования приложений, выполняющих обмен данными при помощи

протокола TCP/IP, запуская сервер и клиент на одном и том же компьютере.

После заполнения структуры с адресной информацией функция connect

создает канал связи с сервером.

2. Передача и прием данных

После того как канал создан, можно начинать передачу данных. Для

передачи данных при помощи протокола гарантированной доставки TCP вы

можете воспользоваться функциями send и recv , которые входят в

программный интерфейс Windows Sockets.

Функция передачи данных send имеет четыре параметра - дескриптор

сокета sock, на котором выполняется передача, адрес буфера buf, содержащего

передаваемое сообщение, размер этого буфера bufsize и флаги flags:

int send (SOCKET sock, const char FAR* buf, int bufsize, int flags);

В приложении CLIENT данные передаются серверу следующим

образом:

char szBuf[80];

lstrcpy(szBuf, "Test string");

send (srv_socket , szBuf, lstrlen(szBuf), 0);

Параметры функции recv , предназначенной для приема данных,

аналогичны параметрам функции send :

int recv (SOCKET sock, char FAR * buf, int bufsize, int flags);

Page 83: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

83

Заметим, что функции recv и send возвращают количество принятых и

переданных байт данных соответственно. Приложение, которое принимает

данные, должно вызывать функцию recv в цикле до тех пор, пока не будут

приняты все переданные данные. При этом на один вызов функции send может

приходиться несколько вызовов функции recv.

В случае ошибки обе эти функции возвращают значение

SOCKET_ERROR. Для анализа причин возникновения ошибки следует

воспользоваться функцией WSAGetLastError .

Приведем список кодов ошибок, которые могут возникать при вызове

команды send:

Код ошибки Описание

WSANOTINITIALISED Перед использованием функции необходимо вызвать

функцию WSAStartup

WSAENETDOWN Сбой в сети

WSAEACCES Указанный адрес является широковещательным (broadcast),

однако перед вызовом функции не был установлен соответствующий флаг

WSAEINTR Работа функции была отменена при помощи функции

WSACancelBlockingCall

WSAEINPROGRESS Выполняется блокирующая функция интерфейса

Windows Sockets

WSAEFAULT Параметр buf указан неправильно (он не указывает на адресное

пространство, принадлежащее приложению)

WSAENETRESET Необходимо сбросить соединение

WSAENOBUFS Возникла блокировка буфера

WSAENOTCONN Сокет не подсоединен

WSAENOTSOCK Указанный в параметре дескриптор не является сокетом

WSAESHUTDOWN Сокет был закрыт функцией shutdown

WSAEWOULDBLOCK Сокет отмечен как неблокирующий, но запрошенная

операция приведет к блокировке

WSAEMSGSIZE Был использован сокет типа SOCK_DGRAM (предназначенный

для передачи датаграмм). При этом размер пакета данных превышает максимально

допустимый для данной реализации интерфейса Windows Sockets

WSAEINVAL Сокет не был подключен функцией bind

WSAECONNABORTED Сбой из-за слишком большой задержки или по другой

причине

WSAECONNRESET Сброс соединения удаленным узлом

Page 84: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

84

При выполнении функции recv могут возникать следующие ошибки:

Код ошибки Описание

WSANOTINITIALISED Перед использованием функции необходимо вызвать

функцию WSAStartup

WSAENETDOWN Сбой в сети

WSAENOTCONN Сокет не подсоединен

WSAEINTR Работа функции была отменена при помощи функции

WSACancelBlockingCall

WSAEINPROGRESS Выполняется блокирующая функция интерфейса

Windows Sockets

WSAENOTSOCK Указанный в параметре дескриптор не является сокетом

WSAESHUTDOWN Сокет был закрыт функцией shutdown

WSAEWOULDBLOCK Сокет отмечен как неблокирующий, но запрошенная

операция приведет к блокировке

WSAEMSGSIZE Размер пакета данных превышает размер буфера, в результате

чего принятый пакет был обрезан

WSAEINVAL Сокет не был подключен функцией bind

WSAECONNABORTED Сбой из-за слишком большой задержки или по другой

причине

WSAECONNRESET Сброс соединения удаленным узлом

Передача и прием данных в цикле может привести к блокировке работы

приложения. Если это неприемлео, следует воспользоваться асинхронным

расширением интерфейса Windows Sockets.

Приложение SERVER демонстрирует асинхронный прием данных.

После установки канала связи оно вызывает функцию WSAAsyncSelect ,

указывая ей в качестве последнего параметра комбинацию констант FD_READ

и FD_CLOSE . При этом функция главного окна приложения будет получать

сообщение WSA_NETEVENT в тот момент времени, когда чтение данных не

вызовет блокировки приложения:

#define WSA_NETEVENT (WM_USER + 2)

rc = WSAAsyncSelect (srv_socket , hWnd, WSA_NETEVENT,

FD_READ | FD_CLOSE );

Page 85: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

85

При необходимости выполнения асинхронной посылки данных можно

указать функции WSAAsyncSelect еще и параметр FD_WRITE .

Если функция WSAAsyncSelect выполнилась успешно, она возвращает

нулевое значение, при ошибке - значение SOCKET_ERROR.

В зависимости от значения последнего параметра могут возникать

разные коды ошибки, которые можно получить при помощи функции

WSAGetLastError. Следующие ошибки могут возникнуть при любом

значении параметра:

Код ошибки Описание

WSANOTINITIALISED Перед использованием функции необходимо вызвать

функцию WSAStartup

WSAENETDOWN Сбой в сети

WSAEINVAL Сокет не был подключен функцией bind

WSAEINPROGRESS Выполняется блокирующая функция интерфейса

Windows Sockets

Дополнительный код ошибки можно получить из параметра lParam при

помощи макрокоманды WSAGETSELECTERROR.

При использовании параметра FD_CONNECT возможно появление

следующих ошибок:

Код ошибки Описание

WSAEADDRINUSE Указанный адрес уже используется

WSAEADDRNOTAVAIL Указанный адрес не доступен

WSAEAFNOSUPPORT Для данного сокета нельзя использовать указанное

семейство адресов

WSAECONNREFUSED Попытка установления канала связи была отвергнута

WSAEDESTADDRREQ Необходимо указать адрес получателя пакета

WSAEFAULT Неправильно указан параметр namelen

WSAEINVAL Сокет уже подключен к адресу

WSAEISCONN Сокет уже подсоединен

WSAEMFILE Больше нет доступных дескрипторов

WSAENETUNREACH Из данного узла и в данное время невозможно получить

доступ к сети

WSAENOBUFS Нет места для размещения буфера

Page 86: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

86

WSAENOTCONN Сокет на подключен

WSAENOTSOCK Указан дескриптор файла, а не сокета

WSAETIMEDOUT При попытке установления канала связи возникла

задержка во времени

Если используется параметр FD_CLOSE, может возникнуть одна из

следующих ошибок:

Код ошибки Описание

WSAENETDOWN Сбой в сети

WSAECONNRESET Сброс соединения удаленным узлом

WSAECONNABORTED Сбой из-за слишком большой задержки или по другой

причине

В том случае, когда указаны параметры FD_READ , FD_WRITE ,

FD_OOB , или FD_ACCEPT , может возникнуть ошибка с кодом

WSAENETDOWN .

Обработчик сообщения WSA_NETEVENT должен выполнить анализ

причины, по которой он был вызван, так как за один вызов функции

WSAAsyncSelect можно задать несколько событий, вызывающих генерацию

сообщения. Этот анализ проводится, например, следующим образом:

void WndProc_OnWSANetEvent(HWND hWnd, UINT msg,

WPARAM wParam, LPARAM lParam)

{

char szTemp[256];

int rc;

// Если на сокете выполняется передача данных,

// принимаем и отображаем эти данные в виде

// текстовой строки

if(WSAGETSELECTEVENT(lParam) = = FD_READ )

{

rc = recv ((SOCKET)wParam, szTemp, 256, 0);

if(rc)

{

Page 87: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

87

szTemp[rc] = '\0';

MessageBox(NULL, szTemp, "Reсeived data", MB_OK);

}

return;

}

// Если соединение завершено, выводим об этом сообщение

else if(WSAGETSELECTEVENT(lParam) == FD_CLOSE )

{

MessageBox(NULL, "Connection closed", "Server", MB_OK);

}

}

Отметим, что параметр wParam содержит дескриптор сокета, на котором

выполняется передача данных, а параметр lParam - код события, которое

произошло в сети.

Полностью программа приема – передачи данных с помощью сокетов

приведена в приложениях SERVER и CLIENT.

Page 88: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

88

2. ПРАКТИЧЕСКИЙ РАЗДЕЛ

2.1. ПРАКТИЧЕСКИЕ ЗАДАНИЯ

по курсу «Компьютерные сети»

ЧАСТЬ 2

―ОСНОВЫ РАЗРАБОТКИ СЕТЕВЫХ ПРИЛОЖЕНИЙ‖

Лабораторно-практическое занятие № 1

Цель работы:

изучение методов разработки серверных и клиентских программ на

основе механизма именованных каналов

Задание на предварительную подготовку:

1. Изучить теоретический материал по электронному конспекту

лекции №1

2. Выполнить задания

Задания:

1. Используя для справки приведенные листинги программ сервера и

клиента именованного канала, разработайте программы сервера и клиента

именованного канала со следующими функциями. Сервер создает

именованный канал, ждет связи клиента с каналом и сообщает об обращении

клиента к каналу или об ошибке. Клиент связывается с каналом и сообщает

об открытии канала или об ошибке.

Продемонстрируйте влияние функции ConnectNamedPipe на

работу канала

2. Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

принимает от него одно или несколько чисел. Клиент связывается с каналом и

отправляет серверу числа.

Продемонстрируйте влияние аргументов функций отправки и

приема информации на передачу данных каналом.

Page 89: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

89

Лабораторно-практическое занятие № 2

Цель работы:

изучение методов разработки серверных и клиентских программ на

основе механизма именованных каналов

Задание на предварительную подготовку:

3. Изучить теоретический материал по электронному конспекту

лекции №2

4. Выполнить задания

Задания:

1. Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

сообщает об обращении клиента к каналу или об ошибке. Клиент связывается

с каналом и сообщает об открытии канала или об ошибке.

Продемонстрируйте, каким образом можно организовать

обращение двух клиентских программ к каналу поочередно, используя

соответствующие аргументы функции создания канала программы

сервера.

2. Продемонстрируйте, как зависит формат имени канала в программе

клиента от его расположения на одном и том же компьютере с сервером или

на разных компьютерах.

3. Сервер создает именованный канал (или общедоступный канал),

ждет соединения клиента, после чего они многократно обмениваются

сообщениями.

Лабораторно-практическое занятие № 3

Задания:

1. Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

сообщает об обращении клиента к каналу или об ошибке. Клиент связывается

с каналом и сообщает об открытии канала или об ошибке.

Page 90: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

90

Затем сервер посылает в канал текстовое сообщение, а клиент сначала

копирует, а затем читает это сообщение.

Лабораторно-практическое занятие № 4

Задания:

1. Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

сообщает об обращении клиента к каналу или об ошибке. Клиент связывается

с каналом и сообщает об открытии канала или об ошибке.

Затем сервер и клиент обмениваются информацией путем

транзакции.

Лабораторно-практическое занятие № 5 и 6

Задание 1.

Используя лекции 1 - 3, создать программу сервера именованного

канала, способного поддерживать связь (обмен сообщениями) с тремя

клиентами.

Задание 2.

Отладить и выполнить программы клиентов (приложения к лекциям 5,

6):

- определяющего состояние именованного канала

- получающего информацию об его неизменяемых атрибутах

- изменяюшего состояние канала.

Лабораторно-практическое занятие № 7 и 8

Цель работы:

изучение методов разработки серверных и клиентских программ на

основе механизма почтовых ящиков

Задание на предварительную подготовку:

5. Изучить теоретический материал по электронному конспекту

лекции №7 и 8

Page 91: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

91

6. Выполнить задания

Задания:

1. Разработайте программу процесса-сервера, который создает почтовый

ящик и сообщает об успешном создании или об ошибке.

2. Разработайте программу процесса-клиента почтового ящика, который

связывается с почтовым ящиком и записывает в него число.

3. Разработайте программу процесса-сервера, который создает почтовый

ящик и читает из него информацию.

4. Разработайте программы сервера и клиента почтового ящика. Клиент

пишет, а сервер читает сообщения.

5. Разработайте программы сервера и клиента, поддерживающие

многократный адресный обмен сообщениями нескольких компьютеров друг с

другом и способных посылать широковещательные сообщения.

Лабораторно-практическое занятие № 9

Цель работы:

изучение методов разработки серверных и клиентских программ на

основе механизма почтовых ящиков

Задания:

1. Разработайте программу процесса-сервера, который создает почтовый

ящик и, используя функцию GetMailslotInfo для получения информации о

сообщениях, хранящихся в почтовом ящике, читает эти сообщения от

нескольких клиентов по мере их поступления. Сервер ждет сообщения

бесконечно долго или заданное конечное время.

Лабораторно-практическое занятие № 10 и 11

Цель работы:

изучение методов разработки серверных и клиентских программ на

основе механизма сокетов

Задание на предварительную подготовку:

Page 92: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

92

7. Изучить теоретический материал по электронному конспекту

лекции №10 и 11

8. Выполнить задания

Задания:

1. Разработайте программу сервера на сокете. Сервер создает сокет и

привязывает его к адресу.

2. Разработайте программу сервера на сокете. Сервер инициализирует

приложение для работы с интерфейсом Windows Sockets, создает сокет,

привязывает его к адресу и организует канал связи с серверной стороны.

3. Разработайте программу клиента на сокете, соединяющегося с

каналом. Используйте готовую программу сервера.

4. Разработайте программы сервера и клиента на сокетах,

обменивающихся информацией.

3. РАЗДЕЛ КОНТРОЛЯ ЗНАНИЙ

3.1. ПРОМЕЖУТОЧНЫЙ КОНТРОЛЬ ЗНАНИЙ

Вопросы к контрольной работе по курсу «Компьютерные сети»

Часть 2

«ОСНОВЫ РАЗРАБОТКИ СЕТЕВЫХ ПРИЛОЖЕНИЙ»

1. Разработка сетевых приложений: назовите и охарактеризуйте

модели передачи данных по каналам связи, применяемые для общения

удаленных процессов. На основе какого уровня протоколов ISO/ OSI

происходит реализация этих моделей?

2. Назовите и кратко охарактеризуйте транспортные протоколы

связи удаленных процессов, предназначенные для обмена сообщениями.

3. Расскажите, какие транспортные протоколы поддерживают

потоковую модель связи удаленных процессов. Кратко охарактеризуйте

их.

Page 93: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

93

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

4. Расскажите о механизме генерации и основном принципе

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

особенности и применение.

5. Расскажите об именованных каналах, стандартах имен,

организации работы именованного канала, перечислите используемые

Win32 API - функции.

6. Расскажите о создании именованных каналов.

Охарактеризуйте аргументы Win32 API - функции создания именованного

канала, используя ее прототип.

7. Расскажите о соединении сервера с именованным каналом,

охарактеризуйте применяемую для соединения Win32 API - функцию,

используя ее прототип.

8. Расскажите об алгоритме соединения клиентов с именованным

каналом. Назовите используемые для этого Win32 API - функции,

охарактеризуйте их аргументы, используя соответствующие прототипы.

9. Win32 API - функция, используемая сервером для ожидания

соединения клиента с именованным каналом. Используя прототип функции

ожидания, охарактеризуйте ее аргументы.

10. Обмен данными по именованному каналу. Синхронный и

асинхронный обмен. Используемые для обмена функции, их аргументы.

11. Расскажите о правилах задания имени канала на стороне

сервера и на стороне клиента

12. Расскажите о копировании данных из именованного канала.

Охарактеризуйте применяемую для этого функцию, расскажите о ее

аргументах и условиях применения, используя прототип.

13. Расскажите о применении функции передачи транзакций по

именованному каналу. Охарактеризуйте аргументы функции и условия ее

применения.

14. Расскажите о функции определения состояния

именованного канала, ее применении.

Почтовые ящики

15. Изложите концепцию почтовых ящиков. Приведите порядок

работы с почтовыми ящиками.

Page 94: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

94

16. Расскажите об операции создания почтового ящика.

Приведите имя функции, охарактеризуйте ее аргументы и условия

применения.

17. Соединение клиентов с почтовым ящиком. Функция,

предназначенная для установления связи клиента с почтовым ящиком, ее

параметры.

18. Запись сообщений в почтовый ящик: процедура записи,

функции, предназначенные для этого, их аргументы и порядок

применения.

19. Чтение сообщений из почтового ящика, функция чтения,

условия применения. Завершение работы почтового ящика.

20. Форматы задания имени почтового ящика

21. Получение информации о почтовом ящике. Используемая

функция, ее прототип.

3.2. ИТОГОВЫЙ КОНТРОЛЬ ЗНАНИЙ (ЭКЗАМЕН)

БЕЛОРУССКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ

Гуманитарный факультет

Кафедра информационных технологий

Специальность: 1 - 31 03 07 Прикладная информатика

Курс: 3

Дисциплина: Компьютерные сети

Заведующий кафедрой: _____________ /В.А.Нифагин/

подпись Инициалы Фамилия

Преподаватель:_____________ /В.В. Корнелюк/

подпись Инициалы Фамилия

Дата утверждения: протокол № от г.

Вопросы к экзамену

Раздел 1. «ОБЩИЕ ПРИНЦИПЫ ПОСТРОЕНИЯ И РАБОТЫ

КОМПЬЮТЕРНЫХ СЕТЕЙ»

1. Классификация сетей. Сетевые сервисы. Методы уплотнения

(мультиплексирования) сигналов в сетях. Сетевые топологии.

Page 95: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

95

2. Семиуровневая модель управления в открытых системах

ISO/OSI.

3. Физический уровень. Виды сообщений в информационных

системах. Способ перевода аналоговой информации в дискретную.

Представление сигнала с помощью гармонических составляющих.

4. Физический уровень. Процесс дискретизации в частотном

представлении. Спектр дискретизованного сигнала. Теорема

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

передачи данных.

5. Физический уровень. Методы передачи дискретных данных на

физическом уровне. Модуляция как способ кодирования. Аналоговая

модуляция. Импульсное кодирование. Спектры модулированных

сигналов при цифровом кодировании (идеализированный потенциальный

код) и аналоговой модуляции (амплитудная модуляция). Полоса

пропускания канала.

6. Канальный уровень. Коды, применяемые в локальных сетях

(NRZ, RZ, Манчестерский код, бифазный код, другие коды).

7. Канальный уровень. Обнаружение и коррекция ошибок.

Корректирующие коды. Коды Хэмминга, их применение.

8. Канальный уровень. Методы доступа к среде. Асинхронная и

синхронная передача. Методы коммутации. Коммутация каналов.

Коммутация сообщений. Коммутация пакетов.

9. Базовые технологии локальных сетей. Новые высокоскоростные

технологии.

10. Сетевой уровень. Передача с установлением соединения и без

установления соединения.

11. Сетевые протоколы. Стеки протоколов. Семейство протоколов

TCP/IP.

12. Протокол межсетевого взаимодействия IP. Фрагментация IP-

пакетов.

13. Адресация в IP-сетях. Использование масок и подсетей.

14. Транспортный уровень. Протоколы TCP и UDP. Протоколы

обмена маршрутной информацией.

Раздел 2. «ОСНОВЫ РАЗРАБОТКИ СЕТЕВЫХ ПРИЛОЖЕНИЙ»

15. Разработка сетевых приложений: назовите и охарактеризуйте две

основные модели передачи данных по каналам связи, применяемые для

Page 96: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

96

общения удаленных процессов. На основе какого уровня протоколов ISO/

OSI происходит реализация этих моделей?

16. Назовите и кратко охарактеризуйте транспортные протоколы

связи удаленных процессов, предназначенные для обмена сообщениями.

17. Расскажите, какие транспортные протоколы поддерживают

потоковую модель связи удаленных процессов. Кратко охарактеризуйте

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

передачи данных и их топологии, кратко охарактеризуйте их. Дайте

определение синхронного и асинхронного обмена данными между

взаимодействующими удаленными потоками.

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

19. Расскажите о механизме генерации и основном принципе

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

особенности и применение.

20. Расскажите об именованных каналах, стандартах имен,

организации работы именованного канала, перечислите используемые

Win32 API - функции.

21. Расскажите о создании именованных каналов. Охарактеризуйте

аргументы Win32 API - функции создания именованного канала, используя

ее прототип.

22. Расскажите о соединении сервера с именованным каналом,

охарактеризуйте применяемую для соединения Win32 API - функцию,

используя ее прототип.

23. Расскажите об алгоритме соединения клиентов с именованным

каналом. Назовите используемые для этого Win32 API - функции,

охарактеризуйте их аргументы, используя соответствующие прототипы.

24. Win32 API - функция, используемая сервером для ожидания

соединения клиента с именованным каналом. Используя прототип функции

ожидания, охарактеризуйте ее аргументы.

25. Обмен данными по именованному каналу. Синхронный и

асинхронный обмен. Используемые для обмена функции, их аргументы.

26. Расскажите о правилах задания имени канала на стороне

сервера и на стороне клиента

27. Расскажите о копировании данных из именованного канала.

Охарактеризуйте применяемую для этого функцию, расскажите о ее

аргументах и условиях применения, используя прототип.

Page 97: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

97

28. Расскажите о применении функции передачи транзакций по

именованному каналу. Охарактеризуйте аргументы функции и условия ее

применения.

29. Расскажите о функции определения состояния именованного

канала, ее применении.

30. Почтовые ящики

31. Изложите концепцию почтовых ящиков. Приведите порядок

работы с почтовыми ящиками.

32. Расскажите об операции создания почтового ящика.

Приведите имя функции, охарактеризуйте ее аргументы и условия

применения.

33. Соединение клиентов с почтовым ящиком. Функция,

предназначенная для установления связи клиента с почтовым ящиком, ее

параметры.

34. Запись сообщений в почтовый ящик: процедура записи,

функции, предназначенные для этого, их аргументы и порядок

применения.

35. Чтение сообщений из почтового ящика, функция чтения, условия

применения. Завершение работы почтового ящика.

36. Форматы задания имени почтового ящика

37. Получение информации о почтовом ящике. Используемая

функция, ее прототип.

38. Сокеты

39. Концепция сокетов. Программный интерфейс Windows Sockets,

предоставляемые им возможности. Принципы организации сокета и

механизм его применения при сетевом программировании.

40. Инициализация приложения, использующего интерфейс

Windows Sockets. Функция WSAStartup, ее аргументы и применение.

Определение структуры WSADATA и указателя на нее.

41. Завершение работы приложения, использующего интерфейс

Windows Sockets. Получение кода ошибки. Функции WSACleanup и

WSAGetLastError, их аргументы и применение.

42. Создание и инициализация сокета. Функция socket, типы

сокетов. Задание параметров сокета. Коды ошибок при создании сокета.

43. Привязка адреса к сокету. Функция bind. Коды ошибок при

привязке адреса.

Page 98: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

98

44. Создание канала связи. Функции listen и accept, их аргументы

и применение. Установление связи с каналом со стороны клиента.

Функция connect.

45. Передача и прием данных при помощи сокета на основе

протокола гарантированной доставки TCP. Функции send и recv.

Практические задания к экзаменационным билетам по дисциплине

«Компьютерные сети»

Билет 1.

Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

сообщает об обращении клиента к каналу или об ошибке. Клиент связывается

с каналом и сообщает об открытии канала или об ошибке.

Билет 2.

Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

сообщает об обращении клиента к каналу или об ошибке. Клиент связывается

с каналом и сообщает об открытии канала или об ошибке.

Продемонстрируйте обращение двух клиентских программ к одному и

тому же экземпляру именованного канала поочередно.

Билет 3.

Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

принимает от него несколько чисел. Клиент связывается с каналом и

отправляет серверу числа.

Продемонстрируйте влияние функции ConnectNamedPipe на работу

канала.

Билет 4.

Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

сообщает об обращении клиента к каналу или об ошибке. Клиент связывается

с каналом и сообщает об открытии канала или об ошибке.

Page 99: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

99

Продемонстрируйте, каким образом можно организовать обращение

двух клиентских программ к каналу поочередно, используя

соответствующие аргументы функции создания канала программы

сервера.

Билет 5.

Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

принимает от него одно или несколько чисел. Клиент связывается с каналом и

отправляет серверу числа.

Продемонстрируйте влияние аргументов функций отправки и приема

информации на передачу данных каналом.

Билет 6

Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

сообщает об обращении клиента к каналу или об ошибке. Клиент связывается

с каналом и сообщает об открытии канала или об ошибке.

Продемонстрируйте, как зависит формат имени канала в программе

клиента от его расположения на одном и том же компьютере с сервером или

на разных компьютерах.

Билет 7

Разработайте программу клиента именованного канала, копирующего,

а затем читающего информацию из канала. Используйте готовую программу

сервера именованного канала для помещения информации в канал.

Билет 8

Разработайте программу клиента именованного канала,

осуществляющего передачу транзакции по именованному каналу.

Используйте готовую программу сервера именованного канала.

Билет 9

Разработайте программу клиента именованного канала, которая

получает информацию о состоянии именованного канала посредством вызова

функции GetNamedPipeHandleState. Используйте готовую программу

сервера именованного канала.

Page 100: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

100

Билет 10

Разработайте программу процесса-сервера, который создает почтовый

ящик и сообщает об успешном создании или об ошибке.

Билет 11

Разработайте программу процесса-клиента почтового ящика, который

связывается с почтовым ящиком и записывает в него число.

Билет 12

Разработайте программу процесса-сервера, который создает почтовый

ящик и читает из него информацию. Используйте готовую программу клиента

почтового ящика, посылающего в почтовый ящик информацию.

Билет 13

Разработайте программы сервера и клиента почтового ящика. Клиент

пишет, а сервер читает информацию.

Билет 14

Разработайте программу процесса-клиента почтового ящика, который

связывается с почтовым ящиком и сообщает об успешной связи или об

ошибке. Используйте готовую программу сервера, создающего почтовый

ящик.

Билет 15

Разработайте программу сервера на сокете. Сервер создает сокет и

привязывает его к адресу.

Билет 16

Разработайте программу сервера на сокете. Сервер инициализирует

приложение для работы с интерфейсом Windows Sockets, создает сокет и

организует канал связи с серверной стороны.

Билет 17

Разработайте программу сервера на сокете. Сервер создает сокет,

организует канал связи, принимает информацию и завершает свою работу.

Используйте готовую программу сокет-клиента.

Page 101: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

101

Билет 18

Разработайте программу сервера на сокете. Сервер создает сокет,

заполняет необходимые данные и привязывает его к адресу.

Билет 19

Разработайте программу клиента на сокете, посылающего информацию

серверу.

Билет 20

Разработайте программы сервера и клиента на сокетах, обменивающихся

информацией.

Билет 21

Разработайте программу клиента на сокете, соединяющегося с каналом.

Используйте готовую программу сервера.

Билет 22

Разработайте программы сервера на сокете, принимающего и

отправляющего сообщения.

Билет 23

Разработайте программу сервера на сокете. Сервер создает сокет,

заполняет необходимые данные и привязывает его к адресу.

Билет 24

Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

принимает от него несколько чисел. Клиент связывается с каналом и

отправляет серверу числа.

Билет 25

Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

сообщает об обращении клиента к каналу или об ошибке. Клиент связывается

с каналом и сообщает об открытии канала или об ошибке.

Page 102: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

102

Билет 26

Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

принимает от него одно или несколько чисел. Клиент связывается с каналом и

отправляет серверу числа.

Продемонстрируйте влияние аргументов функций отправки и приема

информации на передачу данных каналом.

Билет 27

Разработайте программы сервера и клиента именованного канала.

Сервер создает именованный канал, ждет связи клиента с каналом и

принимает от него несколько чисел. Клиент связывается с каналом и

отправляет серверу числа.

Продемонстрируйте влияние функции ConnectNamedPipe на работу

канала.

Билет 28

Разработайте программу процесса-сервера, который создает почтовый

ящик и читает из него информацию. Используйте готовую программу клиента

почтового ящика, посылающего в почтовый ящик информацию.

Билет 29

Разработайте программу клиента именованного канала,

осуществляющего транзакции по именованному каналу. Используйте

готовую программу сервера именованного канала.

Билет 30

Разработайте программу процесса-клиента почтового ящика, который

связывается с почтовым ящиком и сообщает об успешной связи или об

ошибке. Используйте готовую программу сервера, создающего почтовый

ящик.

Page 103: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

103

4. ВСПОМОГАТЕЛЬНЫЙ РАЗДЕЛ

4.1. СПРАВОЧНЫЕ МАТЕРИАЛЫ

ПРИЛОЖЕНИЯ

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

16-01 – Server

16-02 – Client

16-03 – Server

16-04 – Client

16-05 – Server

16-06 – PeekNamedPipe

16-07 – TransactNamedPipe

16-11 – GetNamedPipeInfo

Почтовые ящики

17-01 – Server

17-02 – Client

17-03 – Server

17-04 – Client

17-05 – SetMailslotInfo

Page 104: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

104

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

16-01 – Server #include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hNamedPipe;

// создаем именованный канал для чтения

hNamedPipe = CreateNamedPipe(

"\\\\.\\pipe\\demo_pipe", // имя канала

PIPE_ACCESS_INBOUND, // читаем из канала

PIPE_TYPE_MESSAGE | PIPE_WAIT, // синхронная передача сообщений

1, // максимальное количество экземпляров канала

0, // размер выходного буфера по умолчанию

0, // размер входного буфера по умолчанию

INFINITE, // клиент ждет связь бесконечно долго

NULL // защита по умолчанию

);

// проверяем на успешное создание

if (hNamedPipe == INVALID_HANDLE_VALUE)

{

cerr << "Create named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// ждем, пока клиент свяжется с каналом

cout << "The server is waiting for connection with a client." << endl;

if(!ConnectNamedPipe(

hNamedPipe, // дескриптор канала

NULL // связь синхронная

))

{

cerr << "The connection failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

Page 105: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

105

// читаем данные из канала

for (int i = 0; i < 10; i++)

{

int nData;

DWORD dwBytesRead;

if (!ReadFile(

hNamedPipe, // дескриптор канала

&nData, // адрес буфера для ввода данных

sizeof(nData), // число читаемых байтов

&dwBytesRead, // число прочитанных байтов

NULL // передача данных синхронная

))

{

cerr << "Read file failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим прочитанные данные на консоль

cout << "The number " << nData << " was read by the server" << endl;

}

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "The data are read by the server."<<endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

16-02 – Client #include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hNamedPipe;

char pipeName[] = "\\\\.\\pipe\\demo_pipe";

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_WRITE, // записываем в канал

FILE_SHARE_READ, // разрешаем одновременное чтение из канала

Page 106: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

106

NULL, // защита по умолчанию

OPEN_EXISTING, // открываем существующий канал

0, // атрибуты по умолчанию

NULL // дополнительных атрибутов нет

);

// проверяем связь с каналом

if (hNamedPipe == INVALID_HANDLE_VALUE)

{

cerr << "Connection with the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

return 0;

}

// пишем в именованный канал

for (int i = 0; i < 10; i++)

{

DWORD dwBytesWritten;

if (!WriteFile(

hNamedPipe, // дескриптор канала

&i, // данные

sizeof(i), // размер данных

&dwBytesWritten, // количество записанных байтов

NULL // синхронная запись

))

{

// ошибка записи

cerr << "Writing to the named pipe failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим число на консоль

cout << "The number " << i << " is written to the named pipe."

<< endl;

Sleep(1000);

}

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "The data are written by the client." << endl

<< "Press any key to exit.";

cin.get();

return 0;

}

Page 107: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

107

16-03 – Server #include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

HANDLE hNamedPipe;

DWORD dwBytesRead; // для количества прочитанных байтов

DWORD dwBytesWrite; // для количества записанных байтов

char pchMessage[80]; // для сообщения

int nMessageLength; // длина сообщения

// создаем именованный канал для чтения и записи

hNamedPipe = CreateNamedPipe(

"\\\\.\\pipe\\demo_pipe", // имя канала

PIPE_ACCESS_DUPLEX, // читаем из канала и пишем в канал

PIPE_TYPE_MESSAGE | PIPE_WAIT, // синхронная передача сообщений

1, // максимальное количество экземпляров канала

0, // размер выходного буфера по умолчанию

0, // размер входного буфера по умолчанию

INFINITE, // клиент ждет связь бесконечно долго

NULL // безопасность по умолчанию

);

// проверяем на успешное создание

if (hNamedPipe == INVALID_HANDLE_VALUE)

{

cerr << "Create named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// ждем, пока клиент свяжется с каналом

cout << "The server is waiting for connection with a client." << endl;

if(!ConnectNamedPipe(

hNamedPipe, // дескриптор канала

NULL // связь синхронная

))

{

cerr << "Connect named pipe failed." << endl

<< "The last error code: "<<GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

Page 108: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

108

return 0;

}

// читаем сообщение от клиента

if (!ReadFile(

hNamedPipe, // дескриптор канала

pchMessage, // адрес буфера для ввода данных

sizeof(pchMessage), // количество читаемых байтов

&dwBytesRead, // количество прочитанных байтов

NULL)) // передача данных синхронная

{

cerr << "Data reading from the named pipe failed." << endl

<< "The last error code: "<< GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим полученное от клиента сообщение на консоль

cout << "The server received the message from a client: "

<< endl << '\t' << pchMessage << endl;

// вводим строку

cout << "Input a string: ";

cin.getline(pchMessage, 80);

// определяем длину строки

nMessageLength = strlen(pchMessage) + 1;

// отвечаем клиенту

if (!WriteFile(

hNamedPipe, // дескриптор канала

pchMessage, // адрес буфера для вывода данных

nMessageLength, // количество записываемых байтов

&dwBytesWrite, // количество записанных байтов

NULL // передача данных синхронная

))

{

cerr << "Write file failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим посланное клиенту сообщение на консоль

cout << "The server sent the message to a client: "

<< endl << '\t' << pchMessage << endl;

Page 109: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

109

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

16-04 – Client #include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

char machineName[80];

char pipeName[80];

HANDLE hNamedPipe;

DWORD dwBytesWritten; // для количества записанных байтов

DWORD dwBytesRead; // для количества прочитанных байтов

char pchMessage[80]; // для сообщения

int nMessageLength; // длина сообщения

// вводим имя машины в сети, на которой работает сервер

cout << "Enter a name of the server machine: ";

cin >> machineName;

// подставляем имя машины в имя канала

wsprintf(pipeName, "\\\\%s\\pipe\\demo_pipe", machineName);

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_READ | GENERIC_WRITE, // читаем и записываем в канал

FILE_SHARE_READ | FILE_SHARE_WRITE, // разрешаем чтение и запись

NULL, // безопасность по умолчанию

OPEN_EXISTING, // открываем существующий канал

FILE_ATTRIBUTE_NORMAL, // атрибуты по умолчанию

NULL); // дополнительных атрибутов нет

// проверяем связь с каналом

if (hNamedPipe==INVALID_HANDLE_VALUE)

{

cerr << "Connection with the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

Page 110: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

110

return 0;

}

// вводим строку

cin.get();

cout << "Input a string: ";

cin.getline(pchMessage, 80);

// определяем длину строки

nMessageLength = strlen(pchMessage) + 1;

// пишем в именованный канал

if (!WriteFile(

hNamedPipe, // дескриптор канала

pchMessage, // данные

nMessageLength, // размер данных

&dwBytesWritten, // количество записанных байтов

NULL)) // синхронная запись

{

// ошибка записи

cerr << "Write file failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим посланное сообщение на консоль

cout << "The client sent the message to a server: "

<< endl << '\t' << pchMessage << endl;

// читаем из именованного канала

if (!ReadFile(

hNamedPipe, // дескриптор канала

pchMessage, // данные

sizeof(pchMessage), // размер данных

&dwBytesRead, // количество записанных байтов

NULL)) // синхронное чтение

{

// ошибка чтения

cerr << "Read file failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим полученное сообщение на консоль

cout << "The client received the message from a server: "

Page 111: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

111

<< endl << '\t' << pchMessage << endl;

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

16-05 – Server #include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

HANDLE hNamedPipe;

SECURITY_ATTRIBUTES sa; // атрибуты безопасности

SECURITY_DESCRIPTOR sd; // дескриптор безопасности

DWORD dwBytesRead; // для количества прочитанных байтов

DWORD dwBytesWrite; // для количества записанных байтов

char pchMessage[80]; // для сообщения

int nMessageLength; // длина сообщения

// инициализация атрибутов безопасности

sa.nLength = sizeof(sa);

sa.bInheritHandle = FALSE; // дескриптор канала ненаследуемый

// инициализируем дескриптор безопасности

InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION);

// разрешаем доступ к именованному каналу всем пользователям

SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE);

sa.lpSecurityDescriptor = &sd;

// создаем именованный канал для чтения и записи

hNamedPipe = CreateNamedPipe(

"\\\\.\\pipe\\demo_pipe", // имя канала

PIPE_ACCESS_DUPLEX, // читаем из канала и пишем в канал

PIPE_TYPE_MESSAGE | PIPE_WAIT, // синхронная передача сообщений

1, // максимальное количество экземпляров канала

0, // размер выходного буфера по умолчанию

0, // размер входного буфера по умолчанию

INFINITE, // клиент ждет связь бесконечно долго

&sa // доступ для всех пользователей

);

// проверяем на успешное создание

if (hNamedPipe == INVALID_HANDLE_VALUE)

{

Page 112: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

112

cerr << "Create named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// ждем, пока клиент свяжется с каналом

cout << "The server is waiting for connection with a client." << endl;

if(!ConnectNamedPipe(

hNamedPipe, // дескриптор канала

NULL // связь синхронная

))

{

cerr << "Connect named pipe failed." << endl

<< "The last error code: "<<GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// читаем сообщение от клиента

if (!ReadFile(

hNamedPipe, // дескриптор канала

pchMessage, // адрес буфера для ввода данных

sizeof(pchMessage), // количество читаемых байтов

&dwBytesRead, // количество прочитанных байтов

NULL)) // передача данных синхронная

{

cerr << "Data reading from the named pipe failed." << endl

<< "The last error code: "<< GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим полученное от клиента сообщение на консоль

cout << "The server received the message from a client: "

<< endl << '\t' << pchMessage << endl;

// вводим строку

cout << "Input a string: ";

cin.getline(pchMessage, 80);

// определяем длину строки

nMessageLength = strlen(pchMessage) + 1;

// отвечаем клиенту

Page 113: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

113

if (!WriteFile(

hNamedPipe, // дескриптор канала

pchMessage, // адрес буфера для вывода данных

nMessageLength, // количество записываемых байтов

&dwBytesWrite, // количество записанных байтов

NULL // передача данных синхронная

))

{

cerr << "Write file failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим посланное клиенту сообщение на консоль

cout << "The server sent the message to a client: "

<< endl << '\t' << pchMessage << endl;

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

16-06 – PeekNamedPipe #include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

char machineName[80];

char pipeName[80];

HANDLE hNamedPipe;

DWORD dwBytesRead; // для количества прочитанных байтов

DWORD dwTotalBytesAvail; // количество байтов в канале

DWORD dwBytesLeftThisMessage; // количество непрочитанных байтов

char pchMessage[80]; // для сообщения

// вводим имя машины в сети, на которой работает сервер

cout << "Enter a name of the server machine: ";

cin >> machineName;

Page 114: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

114

cin.get();

// подставляем имя машины в имя канала

wsprintf(pipeName, "\\\\%s\\pipe\\demo_pipe", machineName);

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_READ | GENERIC_WRITE, // читаем и записываем в канал

FILE_SHARE_READ | FILE_SHARE_WRITE, // разрешаем чтение и запись

NULL, // безопасность по умолчанию

OPEN_EXISTING, // открываем существующий канал

FILE_ATTRIBUTE_NORMAL, // атрибуты по умолчанию

NULL); // дополнительных атрибутов нет

// проверяем связь с каналом

if (hNamedPipe==INVALID_HANDLE_VALUE)

{

cerr << "Connection with the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// ждем команду на копирование сообщения из канала

cout << "Press any key to peek a message." << endl;

cin.get();

// копируем информацию из именованного канала

if (!PeekNamedPipe(

hNamedPipe, // дескриптор канала

pchMessage, // данные

sizeof(pchMessage), // размер данных

&dwBytesRead, // количество записанных байтов

&dwTotalBytesAvail, // количество байтов в канале

&dwBytesLeftThisMessage // количество непрочитанных байтов

))

{

// ошибка чтения сообщения

cerr << "Peek named pipe failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим полученное сообщение на консоль

if (dwTotalBytesAvail)

Page 115: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

115

cout << "The peeked message: "

<< endl << '\t' << pchMessage << endl;

else

cout << "There is no mesage." << endl;

// теперь читаем сообщение из именованного канала

if (!ReadFile(

hNamedPipe, // дескриптор канала

pchMessage, // данные

sizeof(pchMessage), // размер данных

&dwBytesRead, // количество записанных байтов

NULL)) // синхронное чтение

{

// ошибка чтения

cerr << "Read file failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим полученное сообщение на консоль

cout << "The client received the message from a server: "

<< endl << '\t' << pchMessage << endl;

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit." << endl;

cin.get();

return 0;

}

16-07 – TransactNamedPipe #include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

char machineName[80];

char pipeName[80];

HANDLE hNamedPipe;

DWORD dwBytesRead; // для количества прочитанных байтов

char pchInBuffer[80]; // для записи сообщения

char pchOutBuffer[80]; // для чтения сообщения

int nMessageLength; // длина сообщения

Page 116: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

116

// вводим имя машины в сети, на которой работает сервер

cout << "Enter a name of the server machine: ";

cin >> machineName;

// подставляем имя машины в имя канала

wsprintf(pipeName, "\\\\%s\\pipe\\demo_pipe", machineName);

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_READ | GENERIC_WRITE, // читаем и записываем в канал

FILE_SHARE_READ | FILE_SHARE_WRITE, // разрешаем чтение и запись

NULL, // безопасность по умолчанию

OPEN_EXISTING, // открываем существующий канал

FILE_ATTRIBUTE_NORMAL, // атрибуты по умолчанию

NULL); // дополнительных атрибутов нет

// проверяем связь с каналом

if (hNamedPipe==INVALID_HANDLE_VALUE)

{

cerr << "Connection with the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// вводим строку

cin.get();

cout << "Input a string: ";

cin.getline(pchInBuffer, 80);

// определяем длину строки

nMessageLength = strlen(pchInBuffer) + 1;

// пишем и читаем из именованного канала одной транзакцией

if (!TransactNamedPipe(

hNamedPipe, // дескриптор канала

&pchInBuffer, // адрес входного буфера канала

nMessageLength, // длина входного сообщения

&pchOutBuffer, // адрес выходного буфера канала

sizeof(pchOutBuffer), // длина выходного буфера канала

&dwBytesRead, // количество прочитанных байтов

NULL)) // передача транзакции синхронная

{

// ошибка транзакции

cerr << "Transact named pipe failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

Page 117: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

117

cin.get();

return 0;

}

// выводим посланное сообщение на консоль

cout << "The sent message: "

<< endl << '\t' << pchInBuffer << endl;

// выводим полученное сообщение на консоль

cout << "The received message: "

<< endl << '\t' << pchOutBuffer << endl;

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

16-11 – GetNamedPipeInfo #include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

char machineName[80];

char pipeName[80];

HANDLE hNamedPipe;

DWORD dwFlags = PIPE_CLIENT_END |

PIPE_TYPE_MESSAGE; // клиент канала и передача данных сообщениями

DWORD dwOutBufferSize; // состояние канала

DWORD dwInBufferSize; // количество экземпляров канала

DWORD dwMaxInstances; // размер буфера клиента канала

// вводим имя машины в сети, на которой работает сервер

cout << "Enter a name of the server machine: ";

cin >> machineName;

cin.get();

// подставляем имя машины в имя канала

wsprintf(pipeName, "\\\\%s\\pipe\\demo_pipe", machineName);

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_READ | GENERIC_WRITE, // читаем и записываем в канал

FILE_SHARE_READ | FILE_SHARE_WRITE, // разрешаем чтение и запись

Page 118: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

118

NULL, // безопасность по умолчанию

OPEN_EXISTING, // открываем существующий канал

FILE_ATTRIBUTE_NORMAL, // атрибуты по умолчанию

NULL); // дополнительных атрибутов нет

// проверяем связь с каналом

if (hNamedPipe==INVALID_HANDLE_VALUE)

{

cerr << "Connection with the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// получаем информацию о канале

if (!GetNamedPipeInfo(

hNamedPipe, // дескриптор именованного канала

&dwFlags, // тип канала

&dwOutBufferSize, // размер выходного буфера

&dwInBufferSize, // размер входного буфера

&dwMaxInstances)) // максимальная количество экземпляров канала

{

cerr << "Get named pipe info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим информацию на консоль

cout << "Out buffer size: " << dwOutBufferSize << endl

<< "In buffer size: " << dwInBufferSize << endl

<< "Max instances: " << dwMaxInstances << endl;

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

Page 119: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

119

Почтовые ящики

17-01 – Server

#include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hMailslot; // дескриптор почтового ящика

// создаем почтовый ящик

hMailslot = CreateMailslot(

"\\\\.\\mailslot\\demo_mailslot", // имя почтового ящика

0, // длина сообщения произвольна

MAILSLOT_WAIT_FOREVER, // ждем сообщения произвольно долго

NULL // безопасность по умолчанию

);

// проверяем на успешное создание

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create mailslot failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

cout << "The mailslot is created." << endl;

cout << "The mailslot is waiting a message." << endl;

// читаем одно целое число из почтового ящика

int nData;

DWORD dwBytesRead;

if (!ReadFile(

hMailslot, // дескриптор почтового ящика

&nData, // адрес буфера для ввода данных

sizeof(nData), // количество читаемых байтов

&dwBytesRead, // количество прочитанных байтов

(LPOVERLAPPED)NULL // передача данных синхронная

))

{

cerr << "Read file failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hMailslot);

cout << "Press any key to finish server.";

Page 120: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

120

cin.get();

return 0;

}

// выводим число на консоль

cout << "The number " << nData << " was read by the server" << endl;

// закрываем дескриптор почтового ящика

CloseHandle(hMailslot);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

17-02 – Client

#include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hMailslot;

char mailslotName[] = "\\\\.\\mailslot\\demo_mailslot";

// связываемся с почтовым ящиком

hMailslot = CreateFile(

mailslotName, // имя почтового ящика

GENERIC_WRITE, // записываем в ящик

FILE_SHARE_READ, // разрешаем одновременное чтение из ящика

NULL, // защита по умолчанию

OPEN_EXISTING, // открываем существующий канал

0, // атрибуты по умолчанию

NULL // дополнительных атрибутов нет

);

// проверяем связь с почтовым ящиком

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create file failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish the client.";

cin.get();

return 0;

Page 121: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

121

}

// вводим целое число

int n;

cout << "Input an integer: ";

cin >> n;

// пишем число в почтовый ящик

DWORD dwBytesWritten;

if (!WriteFile(

hMailslot, // дескриптор почтового ящика

&n, // данные

sizeof(n), // размер данных

&dwBytesWritten, // количество записанных байтов

NULL // синхронная запись

))

{

// ошибка записи

cerr << "Write file failed: " << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish the client.";

cin.get();

CloseHandle(hMailslot);

return 0;

}

// закрываем дескриптор канала

CloseHandle(hMailslot);

// завершаем процесс

cout << "The number is written by the client." << endl

<< "Press any key to exit." << endl;

cin.get();

return 0;

}

17-03 – Server

#include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hMailslot; // дескриптор почтового ящика

DWORD dwNextMessageSize; // размер следующего сообщения

DWORD dwMessageCount; // количество сообщений

Page 122: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

122

// создаем почтовый ящик

hMailslot = CreateMailslot(

"\\\\.\\mailslot\\demo_mailslot", // имя почтового ящика

0, // длина сообщения произвольна

MAILSLOT_WAIT_FOREVER, // ждем сообщения произвольно долго

NULL // безопасность по умолчанию

);

// проверяем на успешное создание

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create mailslot failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

cout << "The mailslot is created." << endl;

// ждем сообщений

cout << "Press any key to read messages." << endl;

cin.get();

// получаем информацию о почтовом ящике

if (!GetMailslotInfo(

hMailslot, // дескриптор почтового ящика

NULL, // максимальный размер сообщения не нужен

&dwNextMessageSize, // размер следующего сообщения

&dwMessageCount, // количество сообщений

NULL)) // интервал ожидания не нужен

{

cerr << "Get mailslot info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

// читаем сообщения

while (dwMessageCount != 0)

{

DWORD dwBytesRead;

char* pchMessage;

// захватываем память для сообщения

pchMessage = (char*) new char[dwNextMessageSize];

// читаем одно сообщение

Page 123: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

123

if (!ReadFile(

hMailslot, // дескриптор канала

pchMessage, // адрес буфера для ввода данных

dwNextMessageSize, // количество читаемых байтов

&dwBytesRead, // количество прочитанных байтов

NULL // передача данных синхронная

))

{

cerr << "Read file failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hMailslot);

cout << "Press any key to finish server.";

cin.get();

return 0;

}

// выводим сообщение на консоль

cout << "The message << " << pchMessage << " >> was read" << endl;

// получаем информацию о следующем сообщении

if (!GetMailslotInfo(

hMailslot, // дескриптор почтового ящика

NULL, // максимальный размер сообщения не нужен

&dwNextMessageSize, // размер следующего сообщения

&dwMessageCount, // количество сообщений

NULL)) // интервал ожидания не нужен

{

cerr << "Get mailslot info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

// освобождаем память для сообщения

delete[] pchMessage;

}

// закрываем дескриптор почтового ящика

CloseHandle(hMailslot);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

Page 124: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

124

17-04 – Client

#include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

HANDLE hMailslot;

char mailslotName[] = "\\\\.\\mailslot\\demo_mailslot";

// связываемся с почтовым ящиком

hMailslot = CreateFile(

mailslotName, // имя почтового ящика

GENERIC_WRITE, // записываем в ящик

FILE_SHARE_READ, // разрешаем одновременное чтение из ящика

NULL, // защита по умолчанию

OPEN_EXISTING, // открываем существующий канал

0, // атрибуты по умолчанию

NULL // дополнительных атрибутов нет

);

// проверяем связь с почтовым ящиком

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create file failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish the client.";

cin.get();

return 0;

}

// вводим количество передаваемых сообщений

int n;

cout << "Input a number of messages: ";

cin >> n;

cin.get();

// пишем сообщения в почтовый ящик

for (int i = 0; i < n; ++i)

{

DWORD dwBytesWritten;

char pchMessage[256];

int nMessageSize;

cout << "Input message: ";

// читаем сообщение

cin.getline(pchMessage, 256);

Page 125: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

125

// определем длину сообщения

nMessageSize = strlen(pchMessage) + 1;

// пишем сообщение

if (!WriteFile(

hMailslot, // дескриптор почтового ящика

pchMessage, // данные

nMessageSize, // размер данных

&dwBytesWritten, // количество записанных байтов

NULL // синхронная запись

))

{

// ошибка записи

cerr << "Write file failed: " << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish the client.";

cin.get();

CloseHandle(hMailslot);

return 0;

}

}

// закрываем дескриптор канала

CloseHandle(hMailslot);

// завершаем процесс

cout << "The messages are written by the client." << endl

<< "Press any key to exit." << endl;

cin.get();

return 0;

}

17-05 – SetMailslotInfo

#include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hMailslot; // дескриптор почтового ящика

DWORD dwReadTimeout; // интервал для ожидания сообщения

// создаем почтовый ящик

hMailslot = CreateMailslot(

"\\\\.\\mailslot\\demo_mailslot", // имя почтового ящика

0, // длина сообщения произвольна

0, // интервал ожидания равен нулю

Page 126: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

126

NULL // защита по умолчанию

);

// проверяем на успешное создание

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create mailslot failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

cout << "The mailslot is created." << endl;

// получаем информацию о почтовом ящике

if (!GetMailslotInfo(

hMailslot, // дескриптор почтового ящика

NULL, // максимальный размер сообщения не нужен

NULL, // размер следующего сообщения не нужен

NULL, // количество сообщений не нужно

&dwReadTimeout)) // интервал ожидания сообщения

{

cerr << "Get mailslot info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

cout << "Read timeout: " << dwReadTimeout << endl;

if (!SetMailslotInfo(

hMailslot, // дескриптор почтового ящика

3000)) // изменяем интервал ожидания

{

cerr << "Set mailslot info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

// получаем информацию о почтовом ящике

if (!GetMailslotInfo(

hMailslot, // дескриптор почтового ящика

NULL, // максимальный размер сообщения не нужен

NULL, // размер следующего сообщения не нужен

Page 127: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

127

NULL, // количество сообщений не нужно

&dwReadTimeout)) // интервал ожидания сообщения

{

cerr << "Get mailslot info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

cout << "Read timeout: " << dwReadTimeout << endl;

// закрываем дескриптор почтового ящика

CloseHandle(hMailslot);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

ФУНКЦИИ

Функции работы с именованными каналами

16-01 – CreateNamedPipe

16-01 – ConnectNamedPipe

16-01 – DisconnectNamedPipe

16-01 – WaitNamedPipe

16-01 – CreateFile

16-02 – Write File

16–01 – ReadFile

Функции работы с почтовыми ящиками

17-01-CreateMailslot

17-02- CreateFile

17-03 – GetMailslotInfo

Page 128: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

128

16-01_CreateNamedPipe

HANDLE CreateNamedPipe(

LPCTSTR lpName, // имя канала

DWORD dwOpenMode, // атрибуты

DWORD dwPipeMode, // режим передачи данных

DWORD nMaxInstances, // максимальное количество экземпляров канала

DWORD nOutBufferSize, // размер выходного буфера

DWORD nInBufferSize, // размер входного буфера

DWORD nDefaultTimeOut, // время ожидания связи с клиентом

LPSECURITY_ATTRIBUTES lpPipeAttributes // атрибуты безопасности

);

Успешное завершение - функция возвращает дескриптор именованного

канала, неудача — одно из двух значений:

- INVALID_HANDLE_VALUE — неудачное завершение; - ERROR_INVALID_PARAMETR — значение параметра nMaxinstances

больше, чем величина PIPE_UNLIMITED_INSTANCES.

Параметры CreateNamedPipe

Параметр lpName – указатель на строку: \\ .\pipe\pipe_name

Параметр dwOpenMode – флаги:

− определения направления передачи данных

- PIPE_ACCESS_DUPLEX — чтение и запись в канал; - PIPE_ACCESS_INBOUND — клиент пишет, а сервер читает

данные

- PIPE_ACCESS_OUTBOUND — сервер пишет, а клиент читает данные.

− определения способа буферизации и синхронизации

- FILE_FLAG_WRITE_THROUGH — запрещает буферизацию при

передаче данных по сети; - FILE_FLAG_OVERLAPPED — разрешает асинхронную передачу

данных по каналу.

− определения атрибутов безопасности

Page 129: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

129

- PIPE_ACCESS_DUPLEX — включает родовые права доступа GENERIC_READ, GENERIC_WRITE и SYNCHRONIZE;

- PIPE_ACCESS_INBOUND — включает родовые права доступа

GENERIC_READ и SYNCHRONIZE; - PIPE_ACCESS_OUTBOUND — включает родовые права доступа

GENERIC_WRITE и SYNCHRONIZE.

Параметр dwPipeMode – флаги:

- определения способов чтения и записи данных в именованный

канал

- PIPE_TYPE_BYTE — запись данных потоком;

- PIPE_TYPE_MESSAGE — запись данных сообщениями; - PIPE_READMODE_BYTE — чтение данных потоком;

- PIPE_READMODE_MESSAGE — чтение данных сообщениями.

- определения синхронизации доступа к именованному каналу

- PIPE_WAIT — синхронная связь с каналом и обмен данными по каналу;

- PIPE_NOWAIT — асинхронная связь с каналом и обмен данными

по каналу.

Параметр nMaxinstances – максимальное число экземпляров

именованного канала (от 1 до PIPE_UNLIMITED__INSTANCES). Каждый экземпляр канала предназначен для обмена данными между сервером и

отдельным клиентом.

Параметры nOutBuffersize и nInBuffersize – соответственно размеры выходного и входного буферов для обмена данными по именованному

каналу (если 0 – значение по умолчанию).

Параметр nDefaultTimeQut – время ожидания клиентом связи с сервером. Это время используется при вызове клиентом функции

WaitNamedPipe, в которой параметр nTimeout имеет значение NMPWAIT_USE_DEFAULT_WAIT.

Параметр lpPipeAttributes – по умолчанию NULL.

Пример использования CreateNamedPipe

int main()

{

HANDLE hNamedPipe;

Page 130: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

130

// создаем именованный канал для чтения

hNamedPipe = CreateNamedPipe(

"\\\\.\\pipe\\demo_pipe", // имя канала

PIPE_ACCESS_INBOUND, // чтение из канала

PIPE_TYPE_MESSAGE | PIPE_WAIT, // сообщениями, синхронно

1, // максимальное количество экземпляров канала

0, // размер выходного буфера по умолчанию

0, // размер входного буфера по умолчанию

INFINITE, // клиент ждет связь бесконечно долго

NULL // защита по умолчанию

);

// проверяем на успешное создание

if (hNamedPipe == INVALID_HANDLE_VALUE)

{

cerr << "Create named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

16-01 – ConnectNamedPipe

BOOL ConnectNamedPipe(

HANDLE hNamedPipe, // дескриптор канала

LPOVERLAPPED lpOverlapped // асинхронная связь

) ;

Успешное завершение - возвращает ненулевое значение, неудача -

FALSE.

Пример использования

// ждем, пока клиент свяжется с каналом

cout << "The server is waiting for connection with a client." << endl;

if(!ConnectNamedPipe(

hNamedPipe, // дескриптор канала

NULL // связь синхронная

Page 131: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

131

))

{

cerr << "The connection failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

16-01 – DisconnectNamedPipe

BOOL DisconnectNamedPipe(

HANDLE hNamedPipe // дескриптор канала

) ;

Успешное завершение - возвращает ненулевое значение, неудача -

FALSE.

16-01 – WaitNamedPipe

BOOL WaitNamedPipe (

LPCTSTR lpNamedPipeName, // указатель на имя канала

DWORD nTimeOut // интервал ожидания

) ;

Успешное завершение - возвращает ненулевое значение, неудача –

FALSE

Параметр lpNamedPipeName – указатель на строку:

\\server_name\pipe\pipe_name

Параметр nTimeOut – время ожидания клиентом связи с сервером.

Определяется в миллисекундах или может быть равен одному из следующих

значений:

Page 132: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

132

- NMPWAIT_USE_DEFAULT_WAIT — интервал времени ожидания

определяется значением параметра nDefauitTimeout, который задается в

функции CreateNamedPipe;

- NMPWAIT_WAIT_FOREVER — бесконечное время ожидания связи с именованным каналом.

16-01 – CreateFile - открытие именованного канала

HANDLE CreateF i1е(

LPCTSTR lpFileName, // указатель на имя канала

DWORD dwDesiredAccess, // чтение или запись в канал

DWORD dwShareMode, // режим совместного использования

LPSECURITY_ATTRIBUTES IpSecurityAttributes, // атрибуты

безопасности

DWORD dwCreationDisposition, // флаг открытия канала

DWORD dwFlagsAndAttributes, // флаги и атрибуты

HANDLE hTemplateFile // дополнительные атрибуты

);

Успешное завершение - функция возвращает дескриптор именованного канала, неудача — значение INVALID_HANDLE_VALUE.

Здесь функция используется для открытия именованного канала, и ее параметры могут принимать следующие значения:

Параметр lpFileName – указатель на имя канала. Если клиент работает на той же машине, что и сервер:

\\.\pipe\pipe_name - канал откроется в режиме передачи данных потоком.

\\server_name\pipe\pipe_name - канал откроется в режиме передачи данных сообщениям.,

Параметр dwDesiredAccess - разрешение доступа для чтения или

записи:

- 0 – разрешает получить только атрибуты канала; - GENERIC_READ — разрешает чтение из канала;

- GENERIC_WRITE — разрешает запись в канал.

Доступ к каналу, заданный этим параметром, должен соответствовать значениям параметра dwOpenMode в функции СreateNamedPipe.

Page 133: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

133

Параметр dwShareMode – режим совместного использования име-

нованного канала:

- 0 – запрещает совместное использование именованного канала,

или любая комбинация следующих значений: - FILE_SHARE_READ — разрешает совместное чтение из канала;

- FILE_SHARE_WRITE — разрешает совместную запись в канал.

Параметр lpSecurityAttributes - по умолчанию NULL.

Параметр dwCreationDisposition – всегда OPEN_EXISTING, т. к. клиент всегда открывает существующий именованный канал.

Параметр dwFlagsAndAttributes – 0 (флаги и атрибуты - по умолчанию).

Параметра hTemplateFile – всегда NULL.

Пример использования

#include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hNamedPipe;

char pipeName[] = "\\\\.\\pipe\\demo_pipe";

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_WRITE, // записываем в канал

FILE_SHARE_READ, // разрешаем одновременное чтение из канала

NULL, // защита по умолчанию

OPEN_EXISTING, // открываем существующий канал

0, // атрибуты по умолчанию

NULL // дополнительных атрибутов нет

);

// проверяем связь с каналом

if (hNamedPipe == INVALID_HANDLE_VALUE)

{

cerr << "Connection with the named pipe failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to exit.";

Page 134: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

134

return 0;

}

16-02 – Write File - запись в именованный канал

BOOL WriteFile(

HANDLE hFile, // дескриптор файла

LPCVOID IpBuffer,// указатель на буфер данных

DWORD nNumberOfBytesToWrite, // количество записываемых

байтов

LPDWORD IpNumberOfBytesWritten, // количество записанных

байтов

LPOVERLAPPED lpOverlapped // указатель на структуру

OVERLAPPED

// используется при асинхронной

записи

);

Успешное завершение – возвращает какое-нибудь ненулевое значение,

неудача — FALSE.

Параметр hFile – дескриптор канала. Он должен быть открыт в режиме

записи.

Параметр IpBuffer – указатель на буфер, данные из которого мы хотим послать в канал.

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

буфер, на который указывает четвертый аргумент, IpNumberOfBytesWritten,

функция WiteFile поместит количество фактически записанных в файл байтов. При работе на платформе Windows 98 этот параметр должен иметь значение,

отличное от NULL. При выполнении функции WriteFile операционная система

записывает по этому адресу ноль, прежде чем выполнить запись данных в файл.

Последний аргумент, lpOverlapped – NULL.

Пример использования

// пишем в именованный канал числа

for (int i = 0; i < 10; i++)

{

DWORD dwBytesWritten;

if (!Write File(

Page 135: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

135

hNamedPipe, // дескриптор канала

&i, // данные

sizeof(i), // размер данных

&dwBytesWritten, // количество записанных байтов

NULL // синхронная запись

))

{

// ошибка записи

cerr << "Writing to the named pipe failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим число на консоль

cout << "The number " << i << " is written to the named pipe."

<< endl;

Sleep(1000);

}

16 – 01 – ReadFile - чтение из именованного канала

BOOL ReadFile(

HANDLE hFile, // дескриптор файла

LPVOID lpBuffer, // указатель на буфер данных, куда читается

блок

DWORD nNumberOfBytesToRead, // количество читаемых

байтов

LPDWORD lpNumberOfBytesRead, // количество прочитанных

байтов

LPOVERLAPPED lpOverlapped // используется при

асинхронной записи

);

Успешное завершение - функция возвращает ненулевое значение, неудача — FALSE.

Параметр lpОverlapped – NULL.

Page 136: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

136

Пример использования

// читаем данные из канала

for (int i = 0; i < 10; i++)

{

int nData;

DWORD dwBytesRead;

if (!ReadFile(

hNamedPipe, // дескриптор канала

&nData, // адрес буфера для ввода данных

sizeof(nData), // число читаемых байтов

&dwBytesRead, // число прочитанных байтов

NULL // передача данных синхронная

))

{

cerr << "Read file failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим прочитанные данные на консоль

cout << "The number " << nData << " was read by the server" << endl;

}

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "The data are read by the server."<<endl;

cout << "Press any key to exit.";

cin.get();

return 0;

}

16-06 - PeekNamedPipe - копирует данные в буфер, не удаляя их из

канала.

BOOL PeekNamedPipe(

HANDLE hNamedPipe, \\ дескриптор канала

LPVOID lpBuffer, \\ буфер данных

DWORD nBufferSize, \\ размер буфера данных

LPDWORD lpBytesRead, \\ количество прочитанных байтов

LPDWORD lpTotalBytesAvail, \\ количество доступных байтов

LPDWORD lpBytesLeftThisMessage \\ количество непрочитанных

байтов

);

Page 137: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

137

В случае успешного завершения функция возвращает ненулевое значение, а в случае неудачи – FALSE. Если данных в канале нет, то функция

немедленно возвращает управление и устанавливает в ноль значения

переменных, на которые указывают параметры lpBytesRead, lpTotalBytesAvail и lpBytesLeftThisMessage.

Параметры функции PeekNamedPipe - дескриптор именованного

канала (должен быть открыт в режиме чтения); область памяти, куда будут читаться данные из канала ( если установлены в NULL, то данные читаться

не будут); размер этой области памяти; количество прочитанных байт (если

данные не читаются, то - NULL); количество доступных байт в канале;

количество непрочитанных байт из сообщения.

Если данные передаются потоком, то функция читает ровно столько

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

буфер данных. В противном случае читается часть сообщения, а количество

непрочитанных байтов возвращается через переменную, на которую

указывает параметр lpBytesLeftThisMessage.

Пример использования

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_READ | GENERIC_WRITE, // читаем и записываем в канал

FILE_SHARE_READ | FILE_SHARE_WRITE, // разрешаем чтение и запись

NULL, // безопасность по умолчанию

OPEN_EXISTING, // открываем существующий канал

FILE_ATTRIBUTE_NORMAL, // атрибуты по умолчанию

NULL); // дополнительных атрибутов нет

// ждем команду на копирование сообщения из канала

cout << "Press any key to peek a message." << endl;

cin.get();

// копируем информацию из именованного канала

if (!PeekNamedPipe(

hNamedPipe, // дескриптор канала

pchMessage, // данные

sizeof(pchMessage), // размер данных

&dwBytesRead, // количество записанных байтов

&dwTotalBytesAvail, // количество байтов в канале

&dwBytesLeftThisMessage // количество непрочитанных байтов

))

Page 138: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

138

{

// ошибка чтения сообщения

cerr << "Peek named pipe failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим полученное сообщение на консоль

if (dwTotalBytesAvail)

cout << "The peeked message: "

<< endl << '\t' << pchMessage << endl;

else

cout << "There is no mesage." << endl;

// теперь читаем сообщение из именованного канала

if (!ReadFile(

hNamedPipe, // дескриптор канала

pchMessage, // данные

sizeof(pchMessage), // размер данных

&dwBytesRead, // количество записанных байтов

NULL)) // синхронное чтение

{

// ошибка чтения

cerr << "Read file failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим полученное сообщение на консоль

cout << "The client received the message from a server: "

<< endl << '\t' << pchMessage << endl;

16-07 - TransactNamedPipe

BOOL TransactNamedPipe (

HANDLE hNamedPipe, // дескриптор именованного канала

LPVOID lpInBuffer, // буфер для записи в канал

DWORD dwInBufferSize, // длина буфера для записи

LPVOID lpOutBuffer, // буфер для чтения из канала

DWORD dwOutBufferSize, // длина буфера для чтения

LPDWORD lpBytesRead, // количество прочитанных байтов

Page 139: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

139

LPOVERLAPPED lpOverlapped // асинхронный доступ к

каналу

);

Функция TransactNamedPipe может использоваться только в том

случае, если сервер при создании именованного канала установил флаг

PIPE_TYPE_MESSAGE.

В случае успешного завершения функция TransactNamedPipe возврашает ненулевое значение, а в случае неудачи — FALSE.

Параметр hNamedPipe - дескриптор именованного канала.

Параметр lpInBuffer должен указывать на буфер, из которого

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

Параметр dwInBufferSize должен содержать длину передаваемого

сообщения в байтах.

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

Параметр dwOutBufferSize должен содержать длину буфера, в который читается сообщение.

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

Параметр lpOverlapped - NULL, что задает синхронную передачу данных.

Если клиент и сервер именованного канала работают на одном ком-пьютере, то для связи клиента с именованным каналом нужно вводить полное

имя компьютера. Если же вместо имени компьютера будет введена точка, то

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

сообщениями. Это вызовет ошибку при передаче данных.

Пример использования

Пример передачи транзакции по именованному каналу (клиент) #include <windows.h>

#include <iostream.h>

#include <string.h>

int main()

{

char machineName[80];

char pipeName[80];

HANDLE hNamedPipe;

Page 140: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

140

DWORD dwBytesRead; // для количества прочитанных байтов

char pchInBuffer[80]; // для записи сообщения

char pchOutBuffer[80]; // для чтения сообщения

int nMessageLength; // длина сообщения

// вводим имя машины в сети, на которой работает сервер

cout << "Enter a name of the server machine: ";

cin >> machineName;

// подставляем имя машины в имя канала

wsprintf(pipeName, "\\\\%s\\pipe\\demo_pipe", machineName);

// связываемся с именованным каналом

hNamedPipe = CreateFile(

pipeName, // имя канала

GENERIC_READ | GENERIC_WRITE, // читаем и записываем в канал

FILE_SHARE_READ | FILE_SHARE_WRITE, // разрешаем чтение и запись

NULL, // безопасность по умолчанию

OPEN_EXISTING, // открываем существующий канал

FILE_ATTRIBUTE_NORMAL, // атрибуты по умолчанию

NULL); // дополнительных атрибутов нет

// вводим строку

cin.get();

cout << "Input a string: ";

cin.getline(pchInBuffer, 80);

// определяем длину строки

nMessageLength = strlen(pchInBuffer) + 1;

// пишем и читаем из именованного канала одной транзакцией

if (!TransactNamedPipe(

hNamedPipe, // дескриптор канала

&pchInBuffer, // адрес входного буфера канала

nMessageLength, // длина входного сообщения

&pchOutBuffer, // адрес выходного буфера канала

sizeof(pchOutBuffer), // длина выходного буфера канала

&dwBytesRead, // количество прочитанных байтов

NULL)) // передача транзакции синхронная

{

// ошибка транзакции

cerr << "Transact named pipe failed: " << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hNamedPipe);

cout << "Press any key to exit.";

cin.get();

return 0;

}

// выводим посланное сообщение на консоль

cout << "The sent message: "

<< endl << '\t' << pchInBuffer << endl;

Page 141: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

141

// выводим полученное сообщение на консоль

cout << "The received message: "

<< endl << '\t' << pchOutBuffer << endl;

// закрываем дескриптор канала

CloseHandle(hNamedPipe);

// завершаем процесс

cout << "Press any key to exit.";

cin.get();

return 0;

}

17-01-CreateMailslot

HANDLE CreateMailslot(

LPCTSTR lpName, // имя почтового ящика

DWORD dwMaxMessageSize // максимальная длина сообщения

DWORD dwReadTimeout, // интервал ожидания

LPSECURITY_ATTRIBUTES IpSecurityAttributes // атрибуты

безопасности

);

В случае успешного завершения эта функция вернет дескриптор

почтового ящика, а в случае неудачи — значение INVALID_HANDLE_VALUE.

Параметры:

Параметр lpName указывает на строку, которая должна иметь вид:

\\. \mailslot\ mailslot_name

Символ "." обозначает локальную машину, так как новый почтовый ящик всегда создается на локальной машине, слово mailslot — фиксировано,

a mailslot_name обозначает имя почтового ящика, которое задается

пользователем и нечувствительно к верхнему и нижнему регистрам.

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

Параметр dwReadTimeout задает в миллисекундах временной интервал, в

течение которого функция ReadFile ждет поступления сообщения в почтовый ящик. Если в этом параметре установлено значение 0, то в случае отсутствия в

почтовом ящике сообщения функция немедленно возвращает управление. Для

Page 142: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

142

задания бесконечного времени ожидания в этом параметре нужно установить

значение MAILSLOT_WAIT_FOREVER.

Несколько процессов могут создать почтовые ящики с одним и тем же

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

таких процессов при условии, что они работают на компьютерах внутри одного

домена. Режим доставки сообщений зависит от режима открытия почтового ящика клиентом.

Пример использования

#include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hMailslot; // дескриптор почтового ящика

// создаем почтовый ящик

hMailslot = CreateMailslot(

"\\\\.\\mailslot\\demo_mailslot", // имя почтового ящика

0, // длина сообщения произвольна

MAILSLOT_WAIT_FOREVER, // ждем сообщения произвольно долго

NULL // безопасность по умолчанию

);

// проверяем на успешное создание

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create mailslot failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

cout << "The mailslot is created." << endl;

cout << "The mailslot is waiting a message." << endl;

Page 143: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

143

17-02- CreateFile

HANDLE CreateFile( LPCTSTR lpFileName, // указатель на имя почтового ящика

DWORD dwDesiredAccess, // чтение или запись з канал DWORD dwShareMode, // режим совместного использования

LPSECURITY_ATTRIBUTES IpSecurityAttributes, // атрибуты

безопасности

DWORD dwCreationDisposition, // флаги открытия почтового ящика DWORD dwFlagsAndAttributes, // флаги и атрибуты

HANDLE hTemplateFile // дополнительные атрибуты

);

В случае успешного завершения эта функция возвращает дескриптор

почтового ящика, а в случае неудачи — значение INVALID_HANDLE_VALUE.

Параметры (функция используется для открытия почтового ящика):

Параметр lpFileName должен указывать на имя почтового ящика,

которое может быть задано в одном из следующих форматов

- почтовый ящик на данном локальном компьютере: :

\\ . \mailslot\имя_почтового_ящика

- почтовый ящик на компьютере с указанным именем:

\\имя_компьтера\ mailslot \имя_почтового_ящика

- почтовый ящик в домене с указанным именем:

\\имя_домена\ mailslot \имя_почтового_ящика

- почтовый ящик в первичном (т.е. данном) домене системы:

\\*\ mailslot \имя_почтового_ящика

Параметр dwDesiredAccess должен иметь значение GENERIC_WRITE,

которое разрешает запись в почтовый ящик.

Параметр dwShareMode определяет режим совместного использования почтового ящика и может принимать любую комбинацию из следующих

значений:

FILE_SHARE_READ — разрешает совместное чтение из почтового ящика;

FILE_SHARE_WRITE — разрешает совместную запись в почтовый ящик.

Page 144: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

144

Параметр lpSecurityAttributes задает атрибуты безопасности

почтового ящика. Пока этот параметр будем устанавливать в NULL.

Для почтового ящика параметр dwCreationDisposition должен быть

равен значению OPEN_EXISTING, т.к. клиент всегда открывает существующий почтовый ящик.

Для почтового ящика параметр dwFlagsAndAttributes можно задать

равным 0, что определяет флаги и атрибуты по умолчанию, или установить в

этом параметре значение FILE_ATTRIBUTE_NORMAL.

Параметр hTemplateFile при работе с почтовыми ящиками не

используется, поэтому в нем устанавливается значение NULL.

Пример использования

#include <windows.h>

#include <iostream.h>

int main()

{

HANDLE hMailslot;

char mailslotName[] = "\\\\.\\mailslot\\demo_mailslot";

// связываемся с почтовым ящиком

hMailslot = CreateFile(

mailslotName, // имя почтового ящика

GENERIC_WRITE, // записываем в ящик

FILE_SHARE_READ, // разрешаем одновременное чтение из ящика

NULL, // защита по умолчанию

OPEN_EXISTING, // открываем существующий канал

0, // атрибуты по умолчанию

NULL // дополнительных атрибутов нет

);

// проверяем связь с почтовым ящиком

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create file failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish the client.";

cin.get();

return 0;

}

Page 145: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

145

17-03 – GetMailslotInfo

BOOL GetMailslotInfo (

HANDLE hMailslot, // дескриптор почтового ящика

LPDWORD IpMaxMessageSize // максимальная длина сообщения

LPDWORD lpNextSize, // длина следующего сообщения

LPDWORD lpMessageCount // количество сообщений

LPDWORD lpReadTimeout // интервал ожидания сообщения

);

В случае успешного завершения эта функция возвращает ненулевое

значение, а в случае неудачи — NULL.

Параметры:

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

Параметр lpMaxMessageSize должен указывать на переменную типа

DWORD, в которую функция GetMailslotInfo поместит максимальную длину

сообщения, которое может быть записано в почтовый ящик. Если это значение не нужно, то этот параметр может быть установлен в NULL.

Параметр lpNextSize должен указывать на переменную типа DWORD, в

которую функция GetMailslotInfo поместит длину следующего сообщения в

почтовом ящике. Если в почтовом ящике нет сообщений, то в этот параметр функция запишет значение MAILSLOT_NO_MESSAGE. Если значение длины

последнего сообщения не нужно, то параметр lpNextSize может быть

установлен в NULL. Параметр lpMessageCount должен указывать на переменную типа

DWORD, .в которую функция GetMailslotInfo поместит количество сообщений,

находящихся в почтовом ящике. Если это значение не нужно, то этот пара-

метр может быть установлен в NULL. Параметр lpReadTimeout должен указывать на переменную типа

DWORD, в которую функция GetMailslotInfo поместит целое число без знака,

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

клиент не запишет сообщение в почтовый ящик. Если это значение не нужно, то

в этом параметре может быть установлено значение NULL.

Пример использования #include <windows.h>

#include <iostream.h>

int main()

{

Page 146: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

146

HANDLE hMailslot; // дескриптор почтового ящика

DWORD dwNextMessageSize; // размер следующего сообщения

DWORD dwMessageCount; // количество сообщений

// создаем почтовый ящик

hMailslot = CreateMailslot(

"\\\\.\\mailslot\\demo_mailslot", // имя почтового ящика

0, // длина сообщения произвольна

MAILSLOT_WAIT_FOREVER, // ждем сообщения произвольно долго

NULL // безопасность по умолчанию

);

// проверяем на успешное создание

if (hMailslot == INVALID_HANDLE_VALUE)

{

cerr << "Create mailslot failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

cout << "The mailslot is created." << endl;

// ждем сообщений

cout << "Press any key to read messages." << endl;

cin.get();

// получаем информацию о почтовом ящике

if (!GetMailslotInfo(

hMailslot, // дескриптор почтового ящика

NULL, // максимальный размер сообщения не нужен

&dwNextMessageSize, // размер следующего сообщения

&dwMessageCount, // количество сообщений

NULL)) // интервал ожидания не нужен

{

cerr << "Get mailslot info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

// читаем сообщения

while (dwMessageCount != 0)

{

DWORD dwBytesRead;

char* pchMessage;

Page 147: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

147

// захватываем память для сообщения

pchMessage = (char*) new char[dwNextMessageSize];

// читаем одно сообщение

if (!ReadFile(

hMailslot, // дескриптор канала

pchMessage, // адрес буфера для ввода данных

dwNextMessageSize, // количество читаемых байтов

&dwBytesRead, // количество прочитанных байтов

NULL // передача данных синхронная

))

{

cerr << "Read file failed." << endl

<< "The last error code: " << GetLastError() << endl;

CloseHandle(hMailslot);

cout << "Press any key to finish server.";

cin.get();

return 0;

}

// выводим сообщение на консоль

cout << "The message << " << pchMessage << " >> was read" << endl;

// получаем информацию о следующем сообщении

if (!GetMailslotInfo(

hMailslot, // дескриптор почтового ящика

NULL, // максимальный размер сообщения не нужен

&dwNextMessageSize, // размер следующего сообщения

&dwMessageCount, // количество сообщений

NULL)) // интервал ожидания не нужен

{

cerr << "Get mailslot info failed." << endl

<< "The last error code: " << GetLastError() << endl;

cout << "Press any key to finish server.";

cin.get();

return 0;

}

// освобождаем память для сообщения

delete[] pchMessage;

}

Page 148: 1 Компьютерные сети БЕЛОРУССКИЙ ... · 2015-12-16 · ЛЕКЦИЯ 10 ЛЕКЦИЯ 11. Компьютерные сети - курс лекций Гуманитарный

Компьютерные сети - курс лекций Гуманитарный факультет

148

4.2. СПИСОК УЧЕБНОЙ ЛИТЕРАТУРЫ И ИНФОРМАЦИОННО-

АНАЛИТИЧЕСКИХ МАТЕРИАЛОВ

1. Побегайло А.П. Системное программирование в Windows. - СПб.,

БХВ-Петербург, 2006. – 1056 с.: ил.

2. Руссинович М. И Соломон Д. Внутреннее устройство Microsoft

Windows: Windows Server 2003, Windows XP и Windows 2000.

Мастер-класс./Пер. с англ. – 4-е изд. – М.: Издательско-торговый дом

«Русская редакция»; СПб.: Питер; 2005. – 992 стр.: ил.

3. Э. Таненбаум. Компьютерные сети. 4-е издание. – СПб.: Питер, 2003.

- 992 с.: ил.

4. Снейдер Й. Эффективное программирование TCP/IP. Библиотека программиста. - СПб.: Питер , 2002. - 320 c.: ил.

5. Гуров И.П. Основы теории информации и передачи сигналов. - СПб.:

BHV-Санкт-Петербург, 2000. - 97 с.: ил.

6. Кулаков Ю.А., Луцкий Г.М. Компьютерные сети – К.: ЮНИОР. - СПб. : Питер, 2002. – 384 с.: ил.

7. Лидовский В.В. Теория информации: Учебное пособие. - М.,

Компания Спутник +, 2004. – 111 с.: ил. 8. В.Г. Олифер, Н.А. Олифер. КОМПЬЮТЕРНЫЕ СЕТИ. Принципы,

технологии, протоколы. УЧЕБНИК . – СПб.: Питер, 2007. – 672 с.: ил.

9. Майстренко Н.В., Майстренко А.В. ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ

САПР. ОПЕРАЦИОННЫЕ СИСТЕМЫ. - Тамбов, Изд. ТГУ, 2007. – 76 с.: ил.