КАТЕГОРИИ: Архитектура-(3434)Астрономия-(809)Биология-(7483)Биотехнологии-(1457)Военное дело-(14632)Высокие технологии-(1363)География-(913)Геология-(1438)Государство-(451)Демография-(1065)Дом-(47672)Журналистика и СМИ-(912)Изобретательство-(14524)Иностранные языки-(4268)Информатика-(17799)Искусство-(1338)История-(13644)Компьютеры-(11121)Косметика-(55)Кулинария-(373)Культура-(8427)Лингвистика-(374)Литература-(1642)Маркетинг-(23702)Математика-(16968)Машиностроение-(1700)Медицина-(12668)Менеджмент-(24684)Механика-(15423)Науковедение-(506)Образование-(11852)Охрана труда-(3308)Педагогика-(5571)Полиграфия-(1312)Политика-(7869)Право-(5454)Приборостроение-(1369)Программирование-(2801)Производство-(97182)Промышленность-(8706)Психология-(18388)Религия-(3217)Связь-(10668)Сельское хозяйство-(299)Социология-(6455)Спорт-(42831)Строительство-(4793)Торговля-(5050)Транспорт-(2929)Туризм-(1568)Физика-(3942)Философия-(17015)Финансы-(26596)Химия-(22929)Экология-(12095)Экономика-(9961)Электроника-(8441)Электротехника-(4623)Энергетика-(12629)Юриспруденция-(1492)Ядерная техника-(1748) |
Именованные каналы в Windows NT
Именованные каналы в Windows NT практически до деталей повторяют управление каналами из OS/2. Отличия касаются только вопросов синхронизации обмена данными и формы записи соответствующих функций и символических констант. Заметим, что именованные каналы полностью отсутствуют в операционных системах подсемейства Windows 9x. Для именованных каналов принята своеобразная концепция общего именования целой группы действительных каналов. За ней стоит желание ограничиться возможно меньшим числом имен для каналов, чтобы эту информацию по человечески было проще использовать. В результате принятой концепции при первом создании именованного канала требуется указывать максимальное число реализаций для одноименной группы. Для расширения потенциальных возможностей можно задать неограниченное число каналов в группе. Отдельный именованный канал передачи данных представляет собой два кольцевых буфера, каждый из которых имеет два указателя, обеспечивающих использование этих буферов. По сложившейся терминологии отдельный именованный канал передачи данных называют экземпляром канала или реализацией канала. Указатели для буфера задают первое следующее свободное место и место, с которого начинается информация, записанная ранее в канал. Кроме того, для каждой пары таких буферов система поддерживает специализированную структуру системных данных, описывающую характеристики такого канала. В случае именованных каналов оба буфера, как системные структуры данных, имеют общий (или скорее одинаковый) хэндл, с помощью которого выполняются операции чтения и записи над этими буферами. (Напомним, что в неименованных каналах каждый из концевых буферов, образующих канал, имеет собственный хэндл, и эти хэндлы для записывающего и принимающего концов буфера различаются.) Для создания именованного канала служит функция с прототипом HANDLE CreateNamedPipe(LPCTSTR lpName, DWORD dwOpenMode, DWORD dwPipeMode, DWORD nMaxInstances, DWORD nOutBufferSize, DWORD nInBufferSize, DWORD nDefaultTimeOut, SECURITY_ATTRIBUTES *pSecurityAttributes), где параметр lpName задает адрес имени канала, параметр dwOpenMode – режим открытия канала, dwPipeMode – режим работы канала, параметр nMaxInstances задает максимальное число экземпляров канала, параметры nOutBufferSize и nInBufferSize определяют размеры выходного и входного буферов канала, а nDefaultTimeOut задает время максимального ожидания, выраженное в миллисекундах. Параметр pSecurityAttributes, специфичный именно для Windows NT, задает адрес атрибутов защиты канала (в большинстве ситуаций достаточно положить этот параметр равным NULL). Значения параметров dwOpenMode и dwPipeMode выражаются через логическое побитовое объединение символических констант. Для задания dwOpenMode используются константы PIPE_ACCESS_DUPLEX, PIPE_ACCESS_INBOUND, PIPE_ACCESS_OUTBOUND и применяемые более редко – FILE_FLAG_WRITE_THROUGH и FILE_FLAG_OVERLAPPED. Режим PIPE_ACCESS_INBOUND аналогичен режиму GENERIC_READ для обычных файлов, а режим PIPE_ACCESS_OUTBOUND аналогичен режиму GENERIC_WRITE обычных файлов. Константа PIPE_ACCESS_DUPLEX аналогична использованию GENERIC_READ | GENERIC_WRITE для обычных файлов. Для задания параметра dwPipeMode предназначены следующие символические константы. Задание типа канала дается взаимоисключаемыми константами PIPE_TYPE_BYTE и PIPE_TYPE_MESSAGE, которые соответственно определяют работу канала в режиме отдельных байтов или сообщений. Режим чтения канала задается константой PIPE_READMODE_BYTE или PIPE_READMODE_MESSAGE. Использование константы PIPE_TYPE_BYTE несовместимо с константой PIPE_READMODE_MESSAG. Работа канала в режиме сообщений, задаваемая константой PIPE_TYPE_MESSAGE, позволяет применять любой из режимов чтения, которые задаются константами PIPE_READMODE_BYTE и PIPE_READMODE_MESSAGE. Практически тип канала определяется тем, в каком виде информация записывается в канал. Это может быть либо неструктурированная запись последовательностей байтов, либо запись в канал структурированных сообщений. Читать же сообщения можно как целиком в приемный буфер либо отдельными байтами, причем в последнем случае до выхода из приемного буфера доходят только собственно данные, а информация заголовка отбрасывается. Кроме типа канала и режима чтения, в параметре задается режим ожидания, для чего служат константы PIPE_WAIT и PIPE_NOWAIT. Первая определяет ожидание при невозможности немедленного выполнения операции с каналом, величина ожидания определяется при этом параметром nDefaultTimeOut. Вторая настраивает канал в режим немедленного выполнения управляющих функций. В последнем случае при невозможности немедленно выполнить указанное действие системная функция выполняется с ошибкой, значение которой можно получить функцией GetLastError(). Последний вариант в Windows NT предоставляется только для обеспечения совместимости и не рекомендован к использованию. Параметр nMaxInstances может задаваться символической константой PIPE_UNLIMITED_INSTANCES, обозначающей неограниченное число экземпляров. Существенно, что при вызове функции создания именованного канала, когда создается очередной экземпляр его, этот параметр должен иметь то же значение, что и для первого экземпляра канала. При неудачном выполнении функция создания именованного канала возвращает нулевое значение, а при удаче – хэндл созданного экземпляра канала. Имя канала в Windows NT задается в универсальной форме вида \\. \pipe\ имяканала где имяканала может включать любые символы за исключением обратной черты (backslash), в частности, цифры и специальные символы. Число символов в таком имени может достигать 256, причем используемые латинские буквы не зависят от регистра (строчные или прописные). Собственно создание экземпляра именованного канала недостаточно для его немедленного использования. С учетом асинхронного характера фактического подключения к каналу двух взаимодействующих процессов для согласованной их подключенности используют универсальную методику запрос-ответ. Для удобства наименований два процесса, участвующие во взаимодействии с помощью именованных каналов, называют серверным и клиентским. Процесс, который создал экземпляр канала, называют серверным, а другой – клиентским. В качестве запроса на образование действующего канала серверный процесс выполняет функцию запроса ConnectNamedPipe, ответом на которую со стороны клиентского процесса должно быть выполнение последним функции открытия файла CreateFile, задающей имя этого именованного канала. Функции ConnectNamedPipe имеет прототип BOOL ConnectNamedPipe(HANDLE hNamedPipe, LPOVERLAPPED lpOverlapped), где первый параметр задает хэндл ранее созданного именованного канала, а второй параметр в простейших случаях полагается равным NULL. Предназначен он для организации синхронизации с помощью объектов событий, где ожидание результата непосредственно запрашивается универсальной функцией WaitForSingleObject или WaitForMultipleObjects. При невозможности немедленного выполнения функция подключения ConnectNamedPipe возвращает значение FALSE, после чего в программе следует предусматривать запрос кода ошибки функцией GetLastError(). Последняя функция возвращает значение ERROR_NO_DATA, если предыдущий клиент закрыл свой конец канала, и значение ERROR_PIPE_CONNECTED, если предыдущий клиент не закрыл свой конец. Собственно операции передачи и приема данных через канал задаются универсальными функциями ReadFile и WriteFile, в которых в качестве хэндла используется хэндл именованного канала. Для порождения ситуации конца файла на принимающем конце программа, задающая действия другого конца, должна выполнить операцию закрытия для дескриптора передающего конца канала. Для более глубокого понимания действий в именованных каналах следует учитывать, что такой канал (точнее экземпляр канала) может находиться в следующих состояниях: подключенном (connected), закрытом (closing), отключенном (disconnected) и прослушивающим (listening). Канал находится в подключенном состоянии, если был создан и подключен серверным процессом и, кроме того, открыт процессом клиента (только подключенные к каналу процессы могут читать из него или записывать данные в в него). Канал находится в закрытом состоянии, если был закрыт клиентским процессом, но все еще не отсоединен серверным процессом. Канал находится в отключенном состоянии, если был создан серверным процессом, но не соединен или явно отсоединен и все еще не присоединен; отсоединенный канал не может принимать запрос открытия функцией CreateFile. Наконец, канал находится в прослушивающем состоянии, если был создан и соединен серверным процессом, но все еще не открыт клиентским процессом; слушающий канал готов принимать запрос на открытие. Если канал находится не в прослушивающем состоянии, функция CreateFile возвращает значение FALSE, после которого запрос функцией GetLastError() возвращает код ошибки, свидетельствующий о занятости канала. Следующая программа, приведенная в листинге 11.4.1, задает функционирование простейшего серверного процесса для именованного канала.
#include <windows.h> #include <stdio.h>
int main() {HANDLE hpipe; BOOL conct; char buffer[512]; DWORD actread, actwrite;
hpipe=CreateNamedPipe("\\\\.\\pipe\\OurPipe", PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE|PIPE_WAIT|PIPE_READMODE_MESSAGE, 1,512,512,2000,NULL); if (hpipe= =INVALID_HANDLE_VALUE) {printf("ErrorCreatePipe, RC=%d\n",GetLastError()); getchar(); return 0;} printf("Waiting for connect...\n"); conct=ConnectNamedPipe(hpipe,NULL); if (!conct) {printf("ErrorConnectPipe, RC=%d\n",GetLastError()); getchar(); CloseHandle(hpipe); return 0;} printf("Connected! Waiting for data...\n"); while (1) {if (ReadFile(hpipe,buffer, 512, &actread,NULL)) {if(!WriteFile(hpipe, buffer, strlen(buffer)+1, &actwrite,NULL)) break; printf("Received data: [%s]\n",buffer); if (!strcmp(buffer,"exit")) break; } else {printf("Error ReadPipe: RC=%d\n",GetLastError()); getchar(); break;} } CloseHandle(hpipe); return 0; } Листинг 11.4.1. Серверный процесс для именованного канала
Открытие именованного канала со стороны клиентского процесса выполняется универсальной функцией CreatePipe, причем в ее параметрах должны быть заданы режимы доступа как по чтению, так и по записи (с помощью символических констант GENERIC_READ и GENERIC_WRITE), а пятый параметр функции (третий с конца и обозначаемый в прототипе как dwCreationDistribution) должен задаваться символической константой OPEN_EXISTING. Имя канала для открытия доступа к нему этой функцией должно задаваться в виде \\servername\pipe\ имяканала где на имяканала накладываются несущественные ограничения, изложенные при описании функции создания канала, а servername задает сетевое имя компьютера, на котором выполняется программа, создавшая канал. Закрытие дескриптора, описывающего конец канала, производится универсальной функцией CloseHandle. В листинге 11.4.2 приведена программа для клиентского процесса, работающего с серверным процессом по предыдущей программе.
#include <windows.h> #include <stdio.h>
int main() {HANDLE hpipe; char buffer[512]; DWORD actread, actwrite;
hpipe=CreateFile("\\\\.\\pipe\\OurPipe", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0,NULL); if (hpipe= =INVALID_HANDLE_VALUE) {printf("ErrorOpenPipe, RC=%d\n",GetLastError()); getchar(); return 0;} printf("Connect with Server!. Type 'exit' for terminate.\n"); while (1) {printf("Input data, please...>\n"); gets(buffer); if (!WriteFile(hpipe,buffer, strlen(buffer)+1, &actwrite,NULL)) break; if(ReadFile(hpipe, buffer, 512,&actread,NULL)) printf("Received back data: [%s]\n",buffer); else {printf("Error ReadPipe: RC=%d\n",GetLastError()); getchar(); break;} if (!strcmp(buffer,"exit")) break; } CloseHandle(hpipe); return 0; } Листинг 11.4.2. Клиентский процесс для именованного канала
При ее выполнении предполагаются, что серверный и клиентский процессы выполняются на одном и том же компьютере, в связи с чем в функции открытия именованного канала используется вырожденный префикс имени серверного компьютера. Программу, использующую открытие канала клиентом с последующим выполнением записи в канал и дальнейшим чтением из него, можно заметно упростить, если воспользоваться специализированной функцией TransactNamedPipe. Последняя функция объединяет в себе все следующие операции, перечисленные в предыдущем предложении: она открывает канал, выполняет запись данных в него и дожидается ответного сообщения из канала, который затем закрывает. Ее ограничением является необходимость использовать передачу данных только сообщениями, для чего канал должен быть соответственно настроен. Функция имеет с прототип BOOL TransactNamedPipe(HANDLE hNamedPipe, VOID* pvWriteBuf, // address of write buffer DWORD cbWriteBuf, // size of the write buffer, in bytes VOID* pvReadBuf, // address of read buffer DWORD cbReadBuf, // size of read buffer, in bytes DWORD* pcbRead, // address of variable for bytes actually read OVERLAPPED* lpo). В ней параметр hNamedPipe задает хэндл именованного канала и должен быть получен в результате выполнения функции CreateFile с именем канала. Параметры pvWriteBuf, pvReadBuf, cbWriteBuf, cbReadBuf задают адреса буферов для записи и чтения данных и их длины соответственно, параметр lpo в простейших случаях задается значением NULL. Практически эта функция объединяет в едином вызове обычные функции WriteFile и ReadFile. Особенностью функции TransactNamedPipe является возможность ее применения только с каналом, работающим в режиме пересылки сообщений. Поэтому для практического применения после открытия доступа к каналу следует переустановить автоматически обеспечиваемый доступ побайтного чтения на режим чтения сообщений. Для этих целей требуется воспользоваться функцией SetNamedPipeHandleState. Она имеет прототип BOOL SetNamedPipeHandleState(HANDLE hNamedPipe, DWORD* pdwMode, DWORD* pcbMaxCollect, DWORD* pdwCollectDataTimeout), где pdwMode задает адрес двойного слова с кодом новых режимов канала, а последующие два параметра имеют достаточно специальный характер и здесь рассматриваться не будут. Достаточно иметь в виду, что для их не использования на месте этих параметров должны стоять значения NULL. Для установки режима чтения сообщений следует использовать символическую константу PIPE_READMODE_MESSAGE. В листинге 11.4.3 приведена программа для клиентского процесса, которая использует функцию транзактивного обмена при работе с каналом.
#include <windows.h> #include <stdio.h>
int main() {HANDLE hpipe; char bufr[512]; char bufw[512]; DWORD actread,mode;
hpipe=CreateFile("\\\\.\\pipe\\OurPipe", GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0,NULL); if (hpipe= =INVALID_HANDLE_VALUE) {printf("ErrorOpenPipe, RC=%d\n",GetLastError()); getchar(); return 0;} printf("Connect with Server!. Type 'exit' for terminate.\n"); mode = PIPE_READMODE_MESSAGE; if (!SetNamedPipeHandleState(hpipe,&mode, NULL, NULL)) {printf("ErrorSetNamedPipeState, RC=%d\n", GetLastError()); getchar(); return 0;}
while (1) {printf("Input data, please...>\n"); gets(bufw); if (TransactNamedPipe(hpipe,bufw, strlen(bufw)+1, bufr, 512, &actread,NULL)) printf("Received back data: [%s]\n",bufr); else {printf("Error ReadPipe: RC=%d\n",GetLastError()); getchar(); break;} if (!strcmp(bufr,"exit")) break; } CloseHandle(hpipe); return 0; } Листинг 11.4.3. Использование функции TransactNamedPipe
Еще раз отметим, что без установления режима чтения из канала на чтение сообщений вызов функции TransactNamedPipe не будет исполнен и вернется код ошибки. Кроме рассмотренных функций для расширенной работы с именованным каналам, имеются функции CallNamedPipe, GetNamedPipeHandleState, GetNamedPipeInfo, PeekNamedPipe, WaitNamedPipe. Первая из них обобщает в одном вызове открытие канала, установку режима и функцию транзакции TransactNamedPipe. Функция PeekNamedPipe позволяет считывать данные из канала, не извлекая их из него, и является средством "подсматривания" за следующими данными. Функции GetNamedPipeHandleState и GetNamedPipeInfo позволяют получать детальную информацию о канале, причем первая из них ориентирована на получение информации об экземпляре канала, изменяемой в процессе эксплуатации последнего, а функция GetNamedPipeInfo дает неизменяемую информацию о канале, заданную при его создании.
Дата добавления: 2014-01-05; Просмотров: 601; Нарушение авторских прав?; Мы поможем в написании вашей работы! Нам важно ваше мнение! Был ли полезен опубликованный материал? Да | Нет |