Использование отображения файла в памяти (разделяемая память)
Система виртуальной памяти в Win32 использует файл подкачки pagefile.sys и может преобразовывать страницы оперативной памяти в страницы файла на диске и наоборот. Система может проецировать на оперативную память не только файл размещения, но и любой другой файл. Это может использоваться для совместного использования памяти несколькими процессами. Механизм отображения файлов может быть использован процессами, работающими только на локальном компьютере; он не используется для передачи данных по сети.
Функция CreateFileMapping создает именованный или неименованный объект-отображение файла:
HANDLE CreateFileMapping(
HANDLE hFile, // дескриптор файла для отображения
LPSECURITY_ATTRIBUTES lpAttributes, // атрибуты безопасности
DWORD flProtect, // флаги защиты
DWORD dwMaximumSizeHigh, // старшие DWORD максимального размера
DWORD dwMaximumSizeLow, // младшие DWORD максимального размера
LPCTSTR lpName // имя объекта-отображения файла
);
Рисунок 3.1 – Схема использования разделяемой памяти
Отображение создается в приложении-сервере:
hMapFile = CreateFileMapping(
INVALID_HANDLE_VALUE, // использование файла подкачки
NULL, // защита по умолчанию
PAGE_READWRITE, // доступ к чтению/записи
0, // макс. размер объекта
1024, // размер буфера
"myFileMapping"); // имя отраженного в памяти объекта
lpD = (D*)MapViewOfFile(hMapFile, //дескриптор отраженного объекта
FILE_MAP_ALL_ACCESS, // разрешение чтения/записи
0, // старший DWORD смещения
0, // младший DWORD смещения
1024); // количество байт для сопоставления
Пример задания основных функций и оконной процедуры приложения-сервера с отображением файла на память дан в приложении В.
Процесс-клиент вызывает функцию OpenFileMapping с именем "myFileMapping", чтобы использовать тот же объект-отображение файла, что и процесс-сервер. Функция MapViewOfFile используется для сопоставления области памяти процесса отображаемому файлу.
hMapFile = OpenFileMapping(
FILE_MAP_ALL_ACCESS, // доступ к чтению/записи
FALSE, // имя не наследуется
"myFileMapping"); // имя "проецируемого " объекта
lpD= (D*) MapViewOfFile(hMapFile, // дескриптор отраженного в памяти объекта
FILE_MAP_ALL_ACCESS, // разрешение чтения/записи
0, // старший DWORD смещения
0, // младший DWORD смещения
1024); // количество байт для сопоставления
Пример задания основных функций и оконной процедуры приложения-клиента с отображением файла на память дан в приложении Г.
Почтовые ящики
Почтовые ящики обеспечивают только однонаправленные соединения. Каждый процесс, который создает почтовый ящик, является «сервером почтовых ящиков» (mailslot server). Другие процессы, называемые «клиентами почтовых ящиков» (mailslot clients), посылают сообщения серверу, записывая их в почтовый ящик. Входящие сообщения всегда дописываются в почтовый ящик и сохраняются до тех пор, пока сервер их не прочтет.
Клиент может посылать сообщения на почтовый ящик, расположенный на том же компьютере, на компьютере в сети, или на все почтовые ящики с одним именем всем компьютерам выбранного домена.
В приложении-сервере необходимо создать почтовый ящик и организовать слежение за его состоянием.
HANDLE Mail;
LPSTR MailslotName = "\\\\.\\mailslot\\$Box_1$"; //имя ящика
DWORD cbRead; // Количество принятых байтов данных
char szBuf[512]; //буфер
Создаем почтовый ящик Mailslot, имеющий имя NameMailslot, в результате в Mail сохраняется хэндл на созданный канал
Mail = CreateMailslot(NameMailslot, 0, MAILSLOT_WAIT_FOREVER, NULL);
Если возникла ошибка, выводим ее код и выходим из функции
if(Mail == INVALID_HANDLE_VALUE) return;
Для слежения за почтовым ящиком - создать таймер с периодом 1 сек. и по срабатыванию таймера вызывать:
Code = GetMailslotInfo(Mail, NULL, &Msg, &Number, NULL); //запрос состояния
// Если в канале есть Mailslot сообщения, читаем его:
if(Number != 0) ReadFile(Mail, szBuf, 512, &cbRead, NULL);
Пример задания основных функций и оконной процедуры приложения-сервера почтового ящика дан в приложении Д.
В приложении-клиенте необходимо соединиться с почтовым ящиком, используя функцию создания файла. Запись в почтовый ящик производится аналогично записи в стандартный дисковый файл.
HANDLE Mail;
LPSTR ServerName = "\\\\.\\mailslot\\$Box_1$";
char szBuf[512];
//Подключение к почтовому ящику
Mail = CreateFile(ServerName, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
// Если возникла ошибка, выводим ее код и выходим из функции
if(Mail == INVALID_HANDLE_VALUE)
{
sprintf(m_mess, "Ошибка создания ящика: %d \r\n", GetLastError());
SendMessage(hwndEdit, WM_SETTEXT, 0, (LPARAM)m_mess);
return;
}
//Запись в почтовый ящик
WriteFile(Mail, szBuf, strlen(szBuf) + 1,&cbWritten, NULL);
Пример задания основных функций и оконной процедуры приложения-клиента почтового ящика дан в приложении Е.
Каналы
Существует два способа организовать двунаправленное соединение с помощью каналов: безымянные и именованные каналы.
Безымянные (или анонимные) каналы позволяют связанным процессам передавать информацию друг другу.
Именованные каналы используются для передачи данных между независимыми процессами или между процессами, работающими на разных компьютерах.
Для связи сервера с несколькими клиентами по одному именованному каналу сервер должен создать несколько экземпляров этого канала. Каждый экземпляр именованного канала создается вызовом функции CreateNamedPipe, в которой некоторые флаги должны быть установлены одинаково для всех экземпляров одного и того же именованного канала. Каждый новый вызов этой функции возвращает новый дескриптор на создаваемый экземпляр именованного канала.
Процесс сервера создает именованный канал с известным именем. Процесс-клиент, зная имя созданного канала, открывает его на своей стороне. После этого между сервером и клиентом создается соединение, по которому может производиться обмен данными в обоих направлениях. Для каждого клиента создается отдельный канал.
Функция CreateNamedPipe создает первый экземпляр именованного канала и возвращает дескриптор. При вызове этой функции указывается также максимально допустимое количество экземпляров каналов, а следовательно, и количество клиентов, одновременная поддержка которых может быть обеспечена.
HANDLE hNamedPipe[2]; //Массив хендлов для 2-х каналов
hNamedPipe[ j ]=CreateNamedPipe("\\\\.\\pipe\\my_pipe",
PIPE_ACCESS_DUPLEX, // Дуплексный доступ к каналу
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES, // Неограниченное количество экземпляров
512, 512, // Размеры буфера отправки и буфера приема
5000, // Время ожидания клиента
NULL); // Без дополнительных атрибутов безопасности
fConnected[ j ] = ConnectNamedPipe(hNamedPipe[ j ], NULL); //false - нет соединения
// Количество байт данных, принятых через канал
ReadFile(hNamedPipe[j],szBuf,512,&cbRead, NULL); //чтение из канала
Пример задания основных функций и оконной процедуры приложения-сервера именованного канала дан в приложении Ж.
В приложении-клиенте доступ к каналу организуется как открытие существующего файла:
hNamedPipe = CreateFile("\\\\.\\pipe\\my_pipe",
GENERIC_READ | GENERIC_WRITE, // доступ на чтение и запись данных
0, //без разделения доступа
NULL, // без дополнительных атрибутов безопасности
OPEN_EXISTING, // открываем существующий канал
0, // задаем атрибуты по умолчанию
NULL); // без файла шаблона
Запись данных в канал:
WriteFile(hNamedPipe, szBuf, strlen(szBuf)+1, &cbWritten , NULL);
Пример задания основных функций и оконной процедуры приложения-клиента именованного канала дан в приложении З.
Сокеты
Спецификация сокетов Windows определяет интерфейс для программирования сети, которая основывается на понятии "сокетов", введенном в Berkeley Software Distribution (BSD). Она включает в себя набор процедур в стиле сокетов Berkeley, а также дополнительный набор функций, специфичных Windows. WinSock (Windows Socket) – это Windows API, который взаимодействует с сетью. С целью экономии более подробное описание использования сокетов для связи между процессами дано в следующем разделе, как часть теоретических основ рассматриваемого примера курсовой работы.