Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабораторная работа#7,8.docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
57.57 Кб
Скачать
  1. Открыть описатель ПЯ, в который нужно записать данные с помощью CreateFile.

  2. Записать данные в ПЯ вызовом функции WriteFile.

  3. Закрыть описатель пя с помощью CloseHandle.

Открытие файла осуществляется при помощи функции

HANDLE CreateFile (

LPCTSTR Name, // имя ПЯ

DWORD DesAccess, // желаемый доступ

DWORD Sharing, // режим разделения

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

DWORD CreatDispos, // создать или открыть

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

HANDLE Template); // шаблон

Поскольку клиент может только записывать данные на сервер, то параметр DesAccess может быть только GENERIC_WRITE. Параметр Sharing должен быть только FILE_SHARE_READ, чтобы сервер мог открывать и читать из почтового ящика. Флаг CreateDispos обязан быть OPEN_EXISTING, а FlagsFILE_ATTRIBUTE_NORMAL.

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

HANDLE WriteFile(

HANDLE File, // описатель файла

LPVOID Buffer, // буфер для сообщения

DWORD BytesToWrite,

// размер буфера (обязательно больше размера сообщения)

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

LPOVERLAPPED Overlap); // асинхронное чтение данных из буфера

Пример клиента почтового ящика приведен ниже.

#include <windows.h>

#include <stdio.h>

void main(int argc, char *argv[])

{

HANDLE Mailslot;

DWORD BytesWritten;

CHAR ServerName[256];

// Вид командной строки для сервера,

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

if (argc < 2)

{

printf("Использование: client <server_name>\n");

return;

}

sprintf(ServerName, "\\\\%s\\Mailslot\\Slot1", argv[1]);

if ((Mailslot = CreateFile(ServerName, GENERIC_WRITE,

FILE_SHARE_READ, NULL, OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL, NULL)) ==

INVALID_HANDLE_VALUE)

{

printf("Создание файла с ошибкой %d\n",

GetLastError());

return;

}

if (WriteFile(Mailslot, "Это тестовое сообщение",

sizeof ("Это тестовое сообщение"),

&BytesWritten, NULL) == 0)

{

printf("Запись в файл с ошибкой %d\n", GetLastError());

return;

}

printf("Записано %d байтов\n", BytesWritten);

CloseHandle(Mailslot);

}

Почти во всех ОС семейства Windows реализован механизм называемый именованными конвейерами (ИК). Они позволяют клиентскому процессу передавать сообщение серверному на одном и том же компьютере или на разных. Разработка приложений, использующих ИК, не требует знания сетевых протоколов. ИК представляют собой простую архитектуру клиент-сервер, обеспечивающую надежную передачу данных.

ИК основаны на интерфейсе файловой системы. Клиенты и серверы используют стандартные функции Win32 API для отправки и получения данных именованным конвейером, а также правила именования файловой системы, а именно:

\\сервер\Pipe\[path]name

где сервер – имя сервера, на котором создается ИК, Pipe – фиксированная обязательная строка, [path]name – имя ИК. В качестве сервера может быть задана точка (.) и имя сервера. ИК нельзя создать на удаленном компьютере.

ИК используют два режима передачи данных: побайтовый и сообщений. В первом случае сообщения передаются непрерывным потоком байтов между клиентом и сервером, при этом они не знают, сколько байтов считывается или записывается в ИК в определенный момент времени. Значит, запись N байтов с одной стороны канала не означает чтения такого же количества байтов с другой стороны. Такой способ позволяет не заботиться о содержимом передаваемых данных. Во втором случае клиент обменивается сервером дискретными блоками данных, при этом сообщения читается целиком.

ИК используют архитектуру клиент-сервер, где данные передаются в одном или двух направлениях. Сервер создает ИК и принимает соединение с клиентом. Клиент ИК – это процесс, устанавливающий соединение с существующим сервером. После этого сервер и клиент могут читать и записывать данные в ИК с помощью функций ReadFile и WriteFile.

Процесс создания сервера заключается в последовательном вызове API-функций:

  1. Создать экземпляр ИК с помощью CreateNamedPipe.

  2. «Слушать» клиентские соединения с помощью ConnectNamedPipe.

  3. Читать и записывать данные в ИК с помощью ReadFile и WriteFile.

  4. Завершить соединение с помощью DisconnectNamedPipe.

  5. Закрыть описатель экземпляра ИК с помощью CloseHandle.

Первая из упомянутых функций определена так.

HANDLE CreateNamedPipe(

LPCTSTR Name, // имя ПЯ

DWORD OpenMode, // направление передачи,

// управление вводом-выводом и безопасность

DWORD PipeMode, // режим операций чтения, записи, ожидания

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

DWORD OutSize, // размер выходящего буфера

DWORD InSize, // размер входящего буфера

DWORD TimeOut, // Время ожидания соединения, в мс.

LPSECURITY_ATTRIBUTES Attrib); //атрибуты безопасности

Флаги режимов могут быть следующими: PIPE_ACCESS_DUPLEX (передача данных в обоих направлениях), PIPE_ACCESS_OUTBOUND (данные передаются от сервера к клиенту) и PIPE_ACCESS_INBOUND (от клиента к серверу), FILE_FLAG_WRITE_THROUGH (функции записи не возвращают значение, пока данные передаются по сети или находятся в буфере), FILE_FLAG_OVERLAPPED (используется перекрытый ввод-вывод), WRITE_DAC (можно изменять список избирательного управления доступом), ACCESS_SYSTEM_SECURITY (можно изменять системный список управления доступом), WRITE_OWNER (можно изменять владельца ИК и групповой идентификатор безопасности).

Параметр PipeMode определяет режимы чтения, записи и ожидания. При создании нужно указать по одному флагу из каждой категории, объединив их операцией «|». Для записи данных флаг PIPE_TYPE_BYTE означает, что данные записываются в ИК потоком байтов, PIPE_TYPE_MESSAGE – потоком сообщений. Для чтения PIPE_READMODE_BYTE означает чтение данных потоком байтов, а PIPE_READMODE_MESSAGE – потоком сообщений. Для операции ожидания PIPE_WAIT включает режим блокировки, PIPE_NOWAIT – отключает режим блокировки.

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

BOOL ConnectNamedPipe(

HANDLE Pipe, // описатель ИК

LPOVERLAPPED Overlap); // асинхронное чтение данных из буфера

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

#include <windows.h>

#include <stdio.h>

void main(void)

{

HANDLE NPipe;

char buffer[256];

DWORD NumberOfBytesRead;

// Создание ИК

if ((NPipe = CreateNamedPipe("\\\\.\\Pipe\\pipe1",

PIPE_ACCESS_DUPLEX, PIPE_TYPE_BYTE |

PIPE_READMODE_BYTE, 1, 0, 0, 2000, NULL)) ==

INVALID_HANDLE_VALUE)

{

printf("Ошибка при создании конвейера %d\n",

GetLastError());

return; }

printf (“Сервер запущен.\n”);

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

{

printf("Ошибка при установлении соединения %d\n",

GetLastError());

CloseHandle (NPipe);

return;

}

if (ReadFile (NPipe, buffer, sizeof (buffer),

&NumberOfBytesRead, NULL) <= 0)

{

printf("Ошибка при чтении из конвейера %d\n",

GetLastError());

CloseHandle (NPipe);

return;

}

printf (“%s\n”, buffer);

if (DisconnectNamedPipe(NPipe) == 0)

{

printf("Ошибка при разрыве соединения %d\n",

GetLastError());

return;

}

CloseHandle (NPipe);

}

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

  1. Для проверки наличия свободного экземпляра ИК нужно вызвать функцию WaitNamedPipe.

  2. Для установления соединения нужно использовать CreateFile.

  3. Читать и записывать данные в ИК с помощью ReadFile и WriteFile.

  4. Завершить соединение с помощью функции CloseHandle.

Перед установлением соединения клиент должен проверить наличие свободного ИК с помощью функции

BOOL WaitNamedPipe (

LPCTSTR Name, // имя ИК

DWORD TimeOut); // величина ожидания свободного ИК

Затем клиент может открыть этот экземпляр ИК с помощью CreateFile, указав в качестве имени название открываемого ИК. Поскольку клиент может при желании читать и/или записывать данные на сервер, то параметр DesAccess может быть GENERIC_WRITE либо GENERIC_READ либо GENERIC_WRITE | GENERIC_READ. Параметр Sharing должен быть только 0, поскольку только один клиент получает доступ к ИК. Флаг CreateDispos обязан быть OPEN_EXISTING, а Flags должен обязательно включать FILE_ATTRIBUTE_NORMAL.

Пример клиента именованного конвейера приведен ниже.

#include <windows.h>

#include <stdio.h>

void main(void)

{

HANDLE NPipe;

DWORD BytesWritten;

if (WaitNamedPipe("\\\\.\\Pipe\\pipe1",

NMPWAIT_WAIT_FOREVER) == 0)

{

printf("Ожидание соединения с ошибкой %d\n",

GetLastError());

return;

}

// создание описателя ИК

if ((NPipe = CreateFile("\\\\.\\Pipe\\pipe1", GENERIC_READ |

GENERIC_WRITE, 0, (LPSECURITY_ATTRIBUTES)NULL,

OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,

(HANDLE)NULL)) == INVALID_HANDLE_VALUE)

{

printf("Создание файла с ошибкой %d\n",

GetLastError());

return;

}

if (WriteFile (NPipe, “Test string”,

strlen (“Test string”),

&BytesWritten, NULL) == 0)

{

printf("Запись в файл с ошибкой %d\n", GetLastError());

CloseHandle (NPipe);

return;

}

printf("Записано %d байтов\n", BytesWritten);

CloseHandle(NPipe);

}