Уровень сложностиПростой
Время на прочтение4 мин
Количество просмотров1.1K
Привет, Хабр!
Часто задаюсь вопросом, как значительно облегчить жизнь пользователя и специалистов технической поддержки? Вопрос бесконечного пути, на котором никогда нельзя останавливаться, каждый раз пробуя новое, не боясь смелых экспериментов.
Меня зовут Владимир Рябовол, в качестве .NET разработчика Страхового Дома ВСК я занимаюсь разработкой desktop-приложения «Агент АРМКоординатор», которое уже показало первые результаты нового качества опыта для специалистов и пользователей.
О блоках:
-
Информация о ПК — просмотр и копирование информации о пользователе, его ПК.
-
Состояние ПК — результаты мониторинга системы, на основании выполнения PowerShell скриптов.
-
Утилиты — состоит из кнопок запуска для «Распознать текст из области», «Распознавать текст из файла», «Сканирование QR-кода», «Конвертер Изображений». Благодаря этим функциям можно выделить любую часть экрана и получить текст из конкретного участка, загрузить изображение для получения текста из него, отсканировать QR-код подключенной к ПК веб-камерой или загрузить изображение с QR-кодом, запустить конвертацию в/из PDF.
-
Ссылки — добавление пользователем кнопки на любой ресурс. Как гиперссылкой на сайт, так и на открытие приложения.
-
Инструменты — пользователь может самостоятельно запускать скрипты для решения типичных проблем самостоятельно.
Ключевой блок приложения создан для мониторинга систем — мы прибегли к использованию мощных инструментов PowerShell. Каждый день наш агент запускает специальные скрипты, дважды в день, и результат выводится в удобном для пользователя формате в блоке Состояние ПК.
Больше всего здесь интересен скрипт, который смотрит атрибуты SMART SSD и выводит оставшееся здоровье диска. В Страховом Доме ВСК доменная структура сети и выполнение скриптов регулируется настройками политики безопасности. Значит, выполнение скриптов обычному пользователю разрешено только в рамках текущего пользователя — для просмотра атрибутов SMART необходимы права администратора. Здесь мы и столкнулись с задачей легального обхода запрета запуска скриптов.
Наш агент установлен на всех ПК компании, развернута и вспомогательная Windows-служба, которая определяет основной ПК пользователя. Служба работает в контексте локального администратора — мы решили воспользоваться её услугами. Для безопасного взаимодействия мы применили именованный канал NamedPipe.
Именованные каналы — это механизм взаимодействия между процессами в операционных системах Windows. Механизм позволяет процессам обмениваться данными друг с другом через специальные «каналы», создаваемые с определенным именем в файловой системе. На стороне службы был развернут сервер NamedPipeServerStream, который ждет подключения клиента, знающего секретное имя.
//Создаём объект безопасности для именованного канала
PipeSecurity ps = new ();
//Получаем идентификатор безопасности для всех пользователей
var sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
NTAccount account = (NTAccount)sid.Translate(typeof(NTAccount));
//Устанавливаем правила доступа для учетной записи NT, позволяя чтение и запись
ps.SetAccessRule(new PipeAccessRule(account, PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow));
//Создаем сервер именованного канала с заданными параметрами, включая объект безопасности
string pipeName = "секретное_имя";
var pipeServer = NamedPipeServerStreamConstructors.New(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.None, 512, 512, ps);
try
{
//Ожидаем подключение клиента к серверу
await pipeServer.WaitForConnectionAsync();
//Создаем поток StreamReader для чтения данных от клиента
var reader = new StreamReader(pipeServer);
var message = await reader.ReadLineAsync();
//Выполняем команду PowerShell полученную от клиента
var result = await RunPowerShell.Run(message);
//Отправляем результат выполнения клиенту
var writer = new StreamWriter(pipeServer);
await writer.WriteAsync(result);
await writer.FlushAsync();
}
catch (Exception ex){ }
//На стороне агента подключение осуществляется через NamedPipeClientStream
//На стороне клиента создаем именованный канал с заданным именем
string pipeName = " секретное_имя";
var pipeClient = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut);
try
{
//Подключаемся к серверу именованного канала
await pipeClient.ConnectAsync();
//Создаем поток StreamWriter для записи данных в канал
var writer = new StreamWriter(pipeClient);
//Отправляем сообщение
await writer.WriteLineAsync(message);
await writer.FlushAsync();
//Создаем поток для чтения ответа
var reader = new StreamReader(pipeClient);
result = reader.ReadToEnd();
}
catch (Exception ex) { }
Скрипты находятся в папке C:\Program Files\VSK\Scripts, что позволяет не беспокоиться о том, что обычный пользователь сможет изменить скрипт или добавить другой. Вместе с тем, в рамках безопасности наши скрипты подписаны сертификатом компании, ведь компании политика запуска скриптов RemoteSigned, не позволяющая запуск сценариев PowerShell, которые не имеют цифровой подписи.
Скрипт, предполагаемый к запуску от учетной записи администратора, должен начинаться с комментария #admin. Агент, при открытии скрипта, смотрит на наличие этого комментария. Если есть, то через NamedPipe он передается на выполнение службе, которая в ответ вернет результат. Если комментарий в начале отсутствует, скрипт будет выполнен от имени пользователя.
В нашем агенте есть и функционал опроса пользователя о его настроении, работоспособности, работе определенных систем. Полученную информацию мы также передаем службе через NamedPipe, которая в дальнейшем шифруется AES-алгоритмом и передается через TCP-протокол на сервер.
Накапливая опыт и пробуя новое, мы продолжаем развивать и совершенствовать наш агент для обеспечения безопасного и эффективного взаимодействия с различными системами и сервисами. В обозримой перспективе ожидается расширение функциональности агента для выполнения других задач, связанных с мониторингом и управлением системами, с учетом требований безопасности и производительности.
Буду держать тебя в курсе, Хабр!
Организация каналов в ОC Windows
Анонимные каналы
Анонимные каналы в Windows — это полудуплексное средство потоковой передачи байтов между родственными процессами. Они функционируют в пределах локальной вычислительной системы и хорошо подходят для перенаправления выходного потока одной программы на вход другой. Анонимные каналы реализованы при помощи именованных каналов с уникальными именами.
Анонимные каналы создаются процессом сервером при помощи функции CreatePipe:
BOOL CreatePipe( PHANDLE hReadPipe, // описатель для чтения PHANDLE hWritePipe, // описатель для записи LPSECURITY_ATTRIBUTES lpPipeAttributes, // атрибуты безопасности DWORD nSize // размер канала );
Функция CreatePipe возвращает два описателя (дескриптора) для чтения и записи в канал. После создания канала необходимо передать клиентскому процессу эти дескрипторы (или один из них), что обычно делается с помощью механизма наследования.
Для наследования описателя нужно, чтобы дочерний процесс создавался функцией CreateProcess с флагом наследования TRUE. Предварительно нужно создать наследуемые описатели. Это можно, например, сделать путем явной спецификации параметра bInheritHandle структуры SECURITY_ATTRIBUTES при создании канала.
Другим способом является создание наследуемого дубликата имеющегося описателя при помощи функции DuplicateHandle и последующая передача его создаваемому процессу через командную строку или каким-либо иным образом.
Получив нужный описатель, клиентский процесс, так же как и сервер, может далее взаимодействовать с каналом при помощи функций ReadFile и WriteFile. По окончании работы с каналом оба процесса должны закрыть описатели при помощи функции CloseHandle.
Прогон программы общения процесса через анонимный канал с самим собой
#include <windows.h> #include <stdio.h> int main() { HANDLE hRead, hWrite; char BufIn[100], *BufOut = "0123456789"; int BufSize = 100; int BytesOut = 10, BytesIn = 5, i; if(!CreatePipe(&hRead, &hWrite, NULL, BufSize)) printf("Create pipe failed.\n"); WriteFile(hWrite, BufOut, BytesOut, &BytesOut, NULL); printf("Write into pipe %d bytes : ", BytesOut); for(i=0; i<BytesOut;i++) printf("%c",BufOut[i]); printf("\n"); ReadFile(hRead, BufIn, BytesIn, &BytesIn, NULL); printf("Read from pipe %d bytes : ", BytesIn); for(i=0; i<5;i++) printf("%c",BufIn[i]); return 0; }
В приведенной программе создается анонимный канал, в него записывается строка цифр, затем часть этой строки читается и выводится на экран.
Прогон программы общения через анонимный канал клиента и сервера
В качестве самостоятельного упражнения рекомендуется организовать через анонимный канал межпроцессное взаимодействие. Процесс-сервер должен создать канал, записать в него информацию и запустить процесс-клиент, задача которого — записанную информацию из канала извлечь.
Именованные каналы
Именованные каналы являются объектами ядра ОС Windows, позволяющими организовать межпроцессный обмен не только в изолированной вычислительной системе, но и в локальной сети. Они обеспечивают дуплексную связь и позволяют использовать как потоковую модель, так и модель, ориентированную на сообщения. Обмен данными может быть синхронным и асинхронным.
Каналы должны иметь уникальные в рамках сети имена в соответствии с правилами именования ресурсов в сетях Windows (Universal Naming Convention, UNC), например, \\ServerName\pipe\PipeName. Для общения внутри одного компьютера имя записывается в форме \\.\pipe\PipeName, где «.» обозначает локальную машину. Слово «pipe» в составе имени фиксировано, а PipeName — имя, задаваемое пользователем. Эти имена, подобно именам открытых файлов, не являются именами объектов. Они относятся к пространству имен под управлением драйверов файловых систем именованных каналов ( \Winnt\System32\Drivers\Npfs.sys ), привязанному к специальному объекту устройству \Device\NamedPipe, на которое есть ссылка в каталоге глобальных имен объектов \??\Pipe (эти последние имена «видит» утилита WinObj).
Имена созданных именованных каналов можно перечислить с помощью свободно распространяемой утилиты pipelist с сайта http://www.sysinternals.com. Поскольку имена каналов интегрированы в общую структуру имен объектов, приложения могут открывать именованные каналы с помощью функции CreateFile и взаимодействовать с ними через функции ReadFile и WriteFile.
Использование именованных каналов
Сервер создает именованный канал при помощи функции CreateNamedPipe (см. MSDN).
Помимо имени канала в форме, описанной выше, в число параметров функции входят: флаг, указывающий модель передачи данных; параметр, определяющий синхронный или асинхронный режим работы канала, а также указывающий, должен ли канал быть односторонним или двухсторонним. Кроме того, имеется необязательный дескриптор защиты, запрещающий несанкционированный доступ к именованному каналу, и параметр, определяющий максимальное число одновременных соединений по данному каналу.
Повторно вызывая CreateNamedPipe, можно создавать дополнительные экземпляры этого же канала.
После вызова CreateNamedPipe сервер выполняет вызов ConnectNamedPipe и ждет отклика от клиентов, которые соединяются с каналом при помощи функции CreateFile или CallNamedPipe, указывая при вызове имя созданного сервером канала. Легальный клиент получает описатель, представляющий клиентскую сторону именованного канала, и работа серверной функции ConnectNamedPipe на этом завершается.
После того как соединение по именованному каналу установлено, клиент и сервер могут использовать его для чтения и записи данных через Win32-функции ReadFile и WriteFile.
Прогон программы общения двух процессов через именованный канал
В качестве упражнения рекомендуется осуществить прогон программы общения клиента и сервера через именованный канал.
Сервер
#include <stdio.h> #include <windows.h> void main() { PROCESS_INFORMATION piProcInfo; STARTUPINFO SI; char * ClientName = "client.exe"; HANDLE hPipe; LPTSTR PipeName = TEXT("\\\\.\\pipe\\MyPipe"); char Buff[255]; DWORD iNumBytesToRead = 255, i; ZeroMemory(&SI, sizeof(STARTUPINFO)); SI.cb = sizeof(STARTUPINFO); ZeroMemory(&piProcInfo, sizeof(piProcInfo)); hPipe = CreateNamedPipe( PipeName, // имя канала PIPE_ACCESS_DUPLEX, // чтение и запись из канала PIPE_TYPE_MESSAGE | // передача сообщений по каналу PIPE_READMODE_MESSAGE | // режим чтения сообщений PIPE_WAIT, // синхронная передача сообщений PIPE_UNLIMITED_INSTANCES, // число экземпляров канала 4096, // размер выходного буфера 4096, // размер входного буфера NMPWAIT_USE_DEFAULT_WAIT, // тайм-аут клиента NULL); // защита по умолчанию if (hPipe == INVALID_HANDLE_VALUE) { printf("CreatePipe failed: error code %d\n", (int)GetLastError()); return; } if((CreateProcess(NULL, ClientName, NULL, NULL, FALSE, 0, NULL, NULL, &SI, &piProcInfo))==0) { printf("create client process: error code %d\n", (int)GetLastError()); return; } if((ConnectNamedPipe(hPipe, NULL))==0) { printf("client could not connect\n"); return; } ReadFile(hPipe, Buff, iNumBytesToRead, &iNumBytesToRead, NULL); for(i=0; i<iNumBytesToRead; i++) printf("%c",Buff[i]); }
7.1.
Клиент
#include <stdio.h> #include <windows.h> void main() { HANDLE hPipe; LPTSTR PipeName = TEXT("\\\\.\\pipe\\MyPipe"); DWORD NumBytesToWrite; char Buff[] = "Message from Client"; hPipe = CreateFile( PipeName, // имя канала GENERIC_READ | // чтение и запись в канал GENERIC_WRITE, 0, // нет разделяемых операций NULL, // защита по умолчанию OPEN_EXISTING, // открытие существующего канала 0, // атрибуты по умолчанию NULL); // нет дополнительных атрибутов WriteFile(hPipe, Buff, strlen(Buff), &NumBytesToWrite, NULL); }
В данном примере сервер создает канал, затем запускает процесс-клиент и ждет соединения. Далее он читает сообщение, посланное клиентом.
Помимо перечисленных выше система представляет еще ряд полезных функций для работы с именованными каналами. Для копирования данных из именованного канала без удаления их из канала используется функция PeekNamedPipe. Функция TransactNamedPipe применяется для объединения операций чтения и записи в канал в одну операцию, которая называется транзакцией. Имеются информационные функции для определения состояния канала, например, GetNamedPipeHandleState или GetNamedPipeInfo. Полный перечень находится в MSDN.
Заключение
Организация совместной деятельности и общения процессов является важной и актуальной задачей. К основным способам межпроцессного обмена традиционно относят каналы и разделяемую память, для организации которых используют разделяемые ресурсы. Анонимные каналы поддерживают потоковую модель, в рамках которой данные представляют собой неструктурированную последовательность байтов.
Именованные каналы, поддерживающие как потоковую модель, так и модель, ориентированную на сообщения, обеспечивают обмен данными не только в изолированной вычислительной среде, но и в локальной сети.
Материал из РУВИКИ — свободной энциклопедии
Текущая версия страницы пока не проверялась опытными участниками и может значительно отличаться от версии, проверенной 15 декабря 2018 года; проверки требуют 3 правки.
Использование именных потоков в GNU/Linux.
В программировании именованный канал или именованный конвейер (англ. named pipe) — один из методов межпроцессного взаимодействия, расширение понятия конвейера в Unix и подобных ОС. Именованный канал позволяет различным процессам обмениваться данными, даже если программы, выполняющиеся в этих процессах, изначально не были написаны для взаимодействия с другими программами. Это понятие также существует и в Microsoft Windows, хотя там его семантика существенно отличается. Традиционный канал — «безымянен», потому что существует анонимно и только во время выполнения процесса. Именованный канал — существует в системе и после завершения процесса. Он должен быть «отсоединён» или удалён, когда уже не используется. Процессы обычно подсоединяются к каналу для осуществления взаимодействия между ними.
Вместо традиционного, безымянного конвейера оболочки (англ. shell pipeline), именованный канал создаётся явно с помощью mknod или mkfifo, и два различных процесса могут обратиться к нему по имени.
Например, можно создать канал и настроить gzip на сжатие того, что туда попадает:
mkfifo pipe gzip -9 -c < pipe > out &
Параллельно в другом процессе можно выполнить:
cat file > pipe
Это приведёт к сжатию передаваемых данных gzip-ом.
В Windows дизайн именованных каналов смещён к взаимодействию «клиент-сервер», и они работают во многом как сокеты: помимо обычных операций чтения и записи, именованные каналы в Windows поддерживают явный «пассивный» режим для серверных приложений (для сравнения: сокет домена UNIX). Windows 95 поддерживает клиенты именованных каналов, а системы ветви Windows NT могут служить также и серверами.
К именованному каналу можно обращаться в значительной степени как к файлу. Можно использовать функции Windows API CreateFile, CloseHandle, ReadFile, WriteFile, чтобы открывать и закрывать канал, выполнять чтение и запись. Функции стандартной библиотеки Си, такие, как fopen, fread, fwrite и fclose, тоже можно использовать, в отличие от сокетов Windows, которые не реализуют использование стандартных файловых операций в сети. Интерфейс командной строки (как в Unix) отсутствует.
Именованные каналы не существуют постоянно и не могут, в отличие от Unix, быть созданы как специальные файлы в произвольной доступной для записи файловой системе, но имеют временные имена (освобождаемые после закрытия последней ссылки на них), которые выделяются в корне файловой системы именованных каналов (англ. named pipe filesystem, NPFS) и монтируются по специальному пути «\\.\pipe\» (то есть у канала под названием «foo» полное имя будет «\\.\pipe\foo»). Анонимные каналы, использующиеся в конвейерах, — это на самом деле именованные каналы со случайным именем.
Именованные каналы обычно не доступны непосредственно пользователю, но есть существенные исключения. Например, средство виртуализации рабочих станций VMWare может открывать эмулируемый последовательный порт для главной системы как именованный канал, а отладчик уровня ядра kd от Microsoft поддерживает именованные каналы в качестве средства сообщения при отладке (фактически, так как kd обычно требует подключения к целевому компьютеру по последовательному порту, VMware и kd можно соединить вместе для отладки драйверов устройств на одном компьютере). Обе программы требуют от пользователя указания имён канала в виде «\\.\pipe\имя».
Именованные каналы в Windows NT могут наследовать контекст безопасности.
Именованные каналы — это также сетевой протокол в SMB, основанный на использовании особой части межпроцессного взаимодействия (IPC). IPC в SMB может бесшовно и прозрачно передавать контекст аутентификации пользователя на другую сторону именованного канала. Наследование аутентификации для именованных каналов Windows NT для пользователя и разработчика настолько прозрачно, что почти незаметно, в связи с чем его часто неправильно понимают.
- Сокеты Беркли
- Сокет домена UNIX
- The Linux Programmer’s Guide: Named Pipes Архивная копия от 5 февраля 2020 на Wayback Machine
- Linux Journal: Introduction to Named Pipes Архивная копия от 11 октября 2017 на Wayback Machine
- MSDN Library: Named Pipes Архивная копия от 11 октября 2017 на Wayback Machine
- Programing with named pipes (from Sun and for Solaris, but a general enough intro for anyone)
Взаимодействие между процессами – ключевая часть многих современных приложений. Один из способов реализовать это взаимодействие – использование именованных каналов. В Windows именованные каналы представляют собой специализированный механизм IPC (Inter-Process Communication), который могут быть использованы для асинхронного или синхронного обмена данными между сервером и клиентами. В POSIX-совместимых системах аналогичная функциональность достигается через такие механизмы, как FIFO (first-in, first-out) файлы, которые предоставляют однонаправленный канал для передачи данных.
Особенности в Windows
- Двунаправленность: Один именованный канал в Windows может использоваться как для чтения, так и для записи данных.
- Настройки безопасности: Можно настроить разрешения для доступа к каналу, контролируя, кто может читать, писать или создавать новые экземпляры канала.
- Типы сообщений: Поддержка как потоковых, так и сообщений с ориентированными передачами данных.
Особенности в POSIX
- Однонаправленность: Каждый FIFO файл предназначен либо для чтения, либо для записи относительно одной стороны (например, сервера).
- Простота: FIFO файлы представляют собой специальные файлы в файловой системе и используются аналогично обычным файлам.
Пример реализации на D
Пример сервера
import std.stdio; import std.process; import std.string; import std.typecons : RefCounted; import std.uuid; void main() { string pipeName = randomUUID().toString; auto serverPipe = NamedPipe(pipeName); writeln("Server: Create a named pipe."); auto childProcess = spawnProcess(["./client", pipeName]); //auto childProcess = spawnProcess(["java", "-jar", "client.jar", pipeName]); // launching a client in java writeln("Server: Launch the client."); auto pipeFile = serverPipe.connect(); writeln("Server: Connection established."); // Sending a message to a client pipeFile.writeln("hello children"); pipeFile.flush(); writeln("Server: Message sent."); // Reading a response from a client string response = pipeFile.readln(); writeln("Server: Response received -", response.strip); childProcess.wait(); writeln("Server: Shutting down."); } struct NamedPipeImpl { immutable string fileName; /// Create a named pipe, and reserve a filename. this()(string name) { version (Windows) { import core.sys.windows.windows; SECURITY_ATTRIBUTES sa; sa.nLength = SECURITY_ATTRIBUTES.sizeof; sa.bInheritHandle = TRUE; // Allow inheritance of handle sa.lpSecurityDescriptor = null; // Use the parent process's security descriptor fileName = `\\.\pipe\` ~ name; auto h = CreateNamedPipe(fileName.toUTF16z(), PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, 1, 4096, 4096, NMPWAIT_USE_DEFAULT_WAIT, &sa) .wenforce("CreateNamedPipe"); f.windowsHandleOpen(h, "wb+"); } else { import core.sys.posix.sys.stat; fileName = `/tmp/` ~ name ~ `.fifo`; mkfifo(fileName.toStringz, S_IWUSR | S_IRUSR); } } /// Wait for a peer to open the other end of the pipe. File connect()() { version (Windows) { import core.sys.windows.windows; import core.sys.windows.windef; BOOL bSuccess = ConnectNamedPipe(f.windowsHandle, null); // "If a client connects before the function is called, the function returns zero // and GetLastError returns ERROR_PIPE_CONNECTED. This can happen if a client // connects in the interval between the call to CreateNamedPipe and the call to // ConnectNamedPipe. In this situation, there is a good connection between client // and server, even though the function returns zero." if (!bSuccess) wenforce(GetLastError() == ERROR_PIPE_CONNECTED, "ConnectNamedPipe"); return f; } else { return File(fileName, "w"); } } ~this() { version (Windows) { // File.~this will take care of cleanup } else fileName.remove(); } private: File f; } alias NamedPipe = RefCounted!NamedPipeImpl; version (Windows): import core.sys.windows.windows; import std.string; class WindowsException : Exception { DWORD code; /// The error code. this(DWORD code, string str=null) { this.code = code; wchar *lpMsgBuf = null; FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, null, code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), cast(LPWSTR)&lpMsgBuf, 0, null); auto message = lpMsgBuf.fromWString(); if (lpMsgBuf) LocalFree(lpMsgBuf); message = strip(message); message ~= format(" (error %d)", code); if (str) message = str ~ ": " ~ message; super(message); } } import std.utf; /// Convert from a potentially zero-terminated wide string. string fromWString(in wchar[] buf) { foreach (i, c; buf) if (!c) return toUTF8(buf[0 .. i]); return toUTF8(buf); } /// Convert from a zero-terminated wide string. string fromWString(in wchar* buf) { if (!buf) return null; const(wchar)* p = buf; for (; *p; p++) { } return toUTF8(buf[0 .. p - buf]); } /// Convert to a zero-terminated wide string. LPCWSTR toWStringz(string s) { return s is null ? null : toUTF16z(s); } T wenforce(T)(T cond, string str=null) { if (cond) return cond; throw new WindowsException(GetLastError(), str); }
В этом коде мы изолировали работу с именованным каналом в структуру NamedPipeImpl
, которая занимается всей работой по созданию, настройке и открытию канала в Windows или POSIX системах. Также для Windows добавлена удобная обработка ошибок. За NamedPipeImpl
и обработку ошибок спасибо создателям библиотеки AE во главе с Владимиром Пантелеевым. NamedPipe
позволяет автоматически освобождать ресурсы при завершении работы с каналом. В SECURITY_ATTRIBUTES
настроено наследование дескриптора процесса дочерним процессом, что позволяет запретить подключение к каналу не дочерних процессов, а также в аргументах функции CreateNamedPipe выставлена возможность создания только одного экземпляра канала, то есть подключиться сможет только 1 клиент. В интернете вы можете найти подробное описание и документацию всех используемых функций и аргументов Windows и POSIX API.
Пример клиента
import std.logger; import std.stdio; void main(string[] args) { Logger logger = new FileLogger("out.log"); string pipePath = r"\\.\pipe\"; if (args.length < 2) { logger.info("Usage: client <channel name>"); return; } string pipeName = pipePath ~ args[1]; auto pipeFile = File(pipeName, "r+"); // Use "r+" to read and write logger.info("Client: Connection established."); // Reading a message from the server string message = pipeFile.readln(); logger.info("Client: Message received - ", message); // Sending a response to the server pipeFile.writeln("hello parent"); pipeFile.flush(); logger.info("Client: Response sent."); logger.info("Client: Shut down."); }
В этом коде клиент работает с каналом как с обычным файлом по определенному пути. Обратите внимание, что для POSIX путь нужно изменить в соответствии с путем, определенным в NamedPipeImpl. Кроме того, в POSIX для двунаправленной связи придется создавать 2 канала, как мы уже писали выше. Один канал на сервере должен быть настроен на запись, другой на прием, соответственно, на клиенте все наоборот. В примере кода клиента имя канала передается просто в аргументах приложения, но можно использовать и другие пути передачи, например анонимные каналы. Запись лога в файл сделана для удобства демонстрации работы клиента.
Пример клиента на Java
Клиент (впрочем, как и сервер) можно написать на любом языке программирования. Для примера, мы использовали Java для создания клиента и подключения к каналу, открытому сервером написанным на D.
import java.io.*; public class Main { public static void main(String[] args) { String pipePath = "\\\\.\\pipe\\"; try (PrintStream log = new PrintStream(new FileOutputStream("client.log"))) { System.setOut(log); System.setErr(log); if (args.length < 1) { System.out.println("Usage: client <channel name>"); return; } String pipeName = pipePath + args[0]; try { // Named pipe connection try (RandomAccessFile pipeFile = new RandomAccessFile(pipeName, "rw")) { System.out.println("Client: Connection established."); // Reading a message from the server String message = pipeFile.readLine(); System.out.println("Client: Message received - " + message); // Отправка ответа серверу pipeFile.writeBytes("hello parent\n"); System.out.println("Client: Response sent."); } } catch (IOException e) { System.err.println("Error when working with named pipe:" + e.getMessage()); } System.out.println("Client: Shut down."); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
Безопасность и шифрование
Обращаем ваше внимание на необходимость обеспечить безопасность передаваемых данных через каналы, особенно при обмене конфиденциальной информацией, особенно в POSIX системах, так как по умолчанию FIFO-каналы никак не защищены. Рекомендуем использовать шифрование данных, например, протокол (Elliptic Curve Diffie-Hellman) для обмена ключами и последующего шифрования передаваемой информации.
Заключение
Именованные каналы представляют собой эффективное средство для обмена данными между процессами. Они обеспечивают гибкость и безопасность при правильной реализации и использовании технологий шифрования. Кроме того, именованные каналы обеспечивают лучшее быстродействие по сравнению с большинством другим методов взаимодействия между процессами, особенно это касается IPC, который был написан специально для данной задачи.
Именованные каналы PIPE используются для гарантированной передачи данных по сети. Создать именованный канал можно только на NT, в операционной среде Windows 95 можно использовать именованный канал только со стороны клиента. Поэтому для проверки данных примеров вам нужен NT. Можно сказать, что это выделенная линия для обмена данными между процессами. В NT можно посмотреть подобные выделенные линии. То есть их количество. Зайдите в Control Panel, выберите Server и щелкните на нем. Появится окно как ниже:
Пункт Open Named Pipes может быть и не такой. Дело в том, что данным механизмом пользуются многие серьезные приложения, например, MS SQL Server. Мы с Вами создадим две простые консольные программы, которые будут эмулировать клиента и сервера. Итак сервер:
// CreateNamedPipe.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "windows.h" #include "iostream.h" void main() { HANDLE hp; hp=CreateNamedPipe("\\\\.\\pipe\\ipctest",PIPE_ACCESS_OUTBOUND, PIPE_TYPE_BYTE | PIPE_NOWAIT,1,0,0,NMPWAIT_USE_DEFAULT_WAIT,NULL); if (hp!=INVALID_HANDLE_VALUE) { int i; cin >> i; } else cout << "Error Create Name Pipe " << endl; }
А теперь клиент:
// CreateFile.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include "windows.h" #include "iostream.h" void main() { HANDLE hp; hp=CreateFile("\\\\Server\\pipe\\ipctest", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hp!=INVALID_HANDLE_VALUE) { int i; cin >> i; } else cout << "Error pipe" << endl; }
Как видите для создание канала надо вызвать функцию CreateNamedPipe, а для клиента создать файл указав сервер Server. Вы должны указывать свой сервер. То есть его имя в домене. А имя \\pipe\\ipctest должно совпадать и в клиенте и в сервере. Это имя канала.
Вот после запуска прогаммы видно, что количество именованных каналов увеличилось на единицу.