На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: jack128, Rouse_, Krid
  
    > Создание именованых каналов. , Named pipes через потоки.
      Создание именованых каналов. Автор: Стас "Hexorg" Пономарёв.

      В этой статье мы будем разбирать связь двух приложений с помощью именованых каналов (named pipes). Рассмотрим типы каналов, а также создадим клиент и сервер.

      1.Для чего именованые каналы нужны:
      Именовыные каналы являются средством «общения» программ. Они широко используются в UNIX-подобных системах, однако и в Windows они нашли применение. С помощью именованых каналов можно передать любую информацию, так как все каналы работают через файлы. Даже для чтения\записи данных в канале используется функция WinAPI для работы с файлами – ReadFile() и WriteFile(). Передаваться может переменная любого типа (Integer, Boolean, Tbitmap и так далее). Однако, надо заметить, что для передачи строки, она должна быть ограничена – String[40]; Иначе при чтении канала будет появляться ошибка.

      2.Пример отправяемой переменной:
      Далее в статье я буду использовать отправляемую переменную типа TpipeData, этот тип будет такой:
      ExpandedWrap disabled
        type TpipeData = packed record  //Имя можно изменить, а тип packed record оставьте
          pEvent:byte;
          ClientName: string[40];                 //Строка фиксированной длины обязательна
          Date:TdateTime;
        End;


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

      ==========СЕРВЕРНАЯ ЧАСТЬ==========

      3.Использование потоков (TThread):
      Как такового события записи в канал нет, поэтому придётся запускать цикл чтения. Как известно, циклы подвешивают программы (а точнее зацикливают). Application.ProcessMessages при работе с функцией ReadFile не поможет, так как ReadFile будет выполнятся до тех пор, пока кто-то что-то не запишет. Поэтому надо использовать потоки (TThread). Хочу заметить, что обращение к визуальным компонентам (формам, кнопкам, лэйблам и так далее) из дополнительных потоков невозможно. Для того, чтобы полученные данные не остались в дополнительном потоке, надо его синхронизироватьс главным. Для этого есть функция TThread.Synchronize(method: TtreadMethod); Параметром этой функции надо передовать Какой-нибудь метод(нами созданный) дополнительного потока, где есть обращение к визуальным компонентам.
      А теперь напишем код нашего потока:

      ExpandedWrap disabled
        type
          TPipeThread = class(TThread)
          private
          PipeData:TPipeData;            //Переменная, описывающая сдруктуру данных канала
          Protected
            procedure Execute; override; //Здесь мы будем создавать канал и читать данные из него
          public
            procedure ShowData;          //Здесь будем передовать данные форме
            constructor Create(CreateSuspended: Boolean); reintroduce; overload; //Создание потока
          end;



      4.Работа с именоваными каналами:
      Напишем нашу процедуру TpipeThread.Execute; но сначала рассмотрим «настройку» наших каналов. Функция CreateNamedPipe доступна только серверу. Ей должны быть переданы следующие параметры:
      1. lpName – имя нашего канала, а точнее его директория. Если сервер будет расположен на той-же машине, что и клиент, то первый параметр должен быть такой: '\\.\PIPE\ИмяКанала', где имя канала – ваше название, только латинскими буквами

      2. dwOpenMode – работа канала может быть:
      PIPE_ACCESS_INBOUND – сервер может только читать канал
      PIPE_ACCESS_OTTBOUND – сервет может только записывать
      PIPE_ACCESS_DUPLEX – сервер может и писать и читать

      3. dwPipeMode – режим канала здесь говорится о синхронности\асинхронности канала, и о том, каким методом будетпроизводится обмен данных в канале. Значения:
      PIPE_WAIT - синхронный канал
      PIPE_NOWAIT – асинхронный канал
      PIPE_READMODE_BYTE – метод чтения - байты
      PIPE_READMODE_MESSAGE – метод чтения пакеты
      PIPE_TYPE_BYTE – тип канала - байты
      PIPE_TYPE_MESSAGE - тип канала пакеты.

      В dwPipeMode надо передавать три параметра, например, в нашем примере: PIPE_WAIT or PIPE_READMODE_MESSAGE or PIPE_TYPE_MESSAGE. А теперь, давайте вспомним наш тип TpipeData – он у нас packed, то есть – запакованный - наш канал работает с пакетами.

      4. hMaxInstances – максимальное число одновременных подключений – любое ваше число, однако можно указать PIPE_UNLIMITED_INSTANCES для безконечного числа клиентов.

      5. nOutBufferSize – разбер буфера чтения. Можно найти так – SizeOf(TpipeData) Это как раз тип нашего буфера.

      6. nInBufferSize – размер буфера записи. Обычно такой же как и буфер чтения.

      7. nDefaultTimeOut – максимальное время чтения (в милисекундах)

      8. lpSecurityArtributes – просто пишем nil.

      Также нам понядобятся фцнкции ConnectNamedPipe, ReadFile и DisconnectNamedPipe, но их параметры мы рассмотрим в процессе написания кода. И так, а теперь наша процедура:

      ExpandedWrap disabled
        procedure TPipeThread.Execute;
        var
          hPipe: THandle; //Указатель на наш канал
          bytesRead: DWORD;  //Количество прочитанных байт
        begin
           hPipe := CreateNamedPipe('\\.\PIPE\OurPipe', //Наше имя
              PIPE_ACCESS_INBOUND,                      // сервер может только читать канал
              PIPE_WAIT or                              // Синхронная работа
              PIPE_READMODE_MESSAGE or                  // метод чтения - пакеты
              PIPE_TYPE_MESSAGE,
              PIPE_UNLIMITED_INSTANCES,                 // Бесконечно много клиентов
              SizeOf(Tpipedata),                        //размер буфера чтения
              SizeOf(Tpipedata),                        // размер буфера записи
              100,                                      // Тайм-аут
              nil);                                     // Артребуты безопасности.
            if hPipe = INVALID_HANDLE_VALUE then Exit;  //Если не удалось создать канал, то выходим
         
            while true do //Теперь читаем, пока не надоест!
            begin
             try
                ConnectNamedPipe(hPipe, nil);           //Подключаемся к каналу, второй параметр нужен только, если вместо PIPE_WAIT вы указали PIPE_NOWAIT
         
                //Далее читаем, параметры – указатель на канал, наш буфер, кол-во прочитанных байт, и последнее опять таки только для PIPE_NOWAIT.
                if ReadFile(hPipe, PipeData, SizeOf(TpipeData), bytesRead, nil) then
                Synchronize(ShowData);                 //Синхронизируемся с главным потоком
              finally
                DisconnectNamedPipe(hPipe);            //Закрываем канал, параметры – только указатель
              end;
             end;
        end;

      5.Ну вот, это большая часть нашего сервера. Функцию ShowData можно сделать такую:

      ExpandedWrap disabled
        procedure TPipeThread.ShowData;
        begin
        Case PipeData.pEvent of
        1:form1.Memo1.Lines.Add('======Событие 1======');
        2:form1.Memo1.Lines.Add('=========Событие 2==========');
        end;
        form1.Memo1.Lines.Add('Программа '+PipeData.Clientname+' открыла канал');
        form1.Memo1.Lines.Add(DateTostr(PipeData.Date));
        form1.Memo1.Lines.Add('');
        end;

      Полный исходник сервера вы найдёте в аттаче!

      ==========КЛИЕНТ==========
      Теперь пришло время написать наш клиент. Так как нам нужо только подключиться и выкинуть данные серверу, а затем сразу отключиться, то весь код клиета можно поместить в одну процедуру:

      ExpandedWrap disabled
        procedure SendToServer; // Замечу, что должна быть переменная такого же типа TpipeData, как и у сервера. Допустим, это Data
        var
          Data:TpipeData;
          hPipe: THandle;
          bytesWritten: DWORD;
        begin
        hPipe := CreateFile('\\.\PIPE\OurPipe',    //Как видите, здесь мы подключаемся даже не к каналу, а к файлу
            GENERIC_WRITE,                         //Только запись
            FILE_SHARE_READ or                     // Обмениваемся чтенью\записью
            FILE_SHARE_WRITE,
            nil,                                   //Артрибуты безопасности
            OPEN_EXISTING,                         // Канал должен быть создан
            0, 0);
        if hPipe = INVALID_HANDLE_VALUE then Exit; //Если произошла ошибка, выходим
         
        Data.pEvent:=0;
        Data.ClientName:='PipeClient';
        Data.Date:=Time;                           //Функция Time записана в модуле SysUtils и возвращает текущее время
                      
        if WriteFile(hPipe, Data, SizeOf(TpipeData), bytesWritten,
            nil) then  DisconnectNamedPipe(hpipe); //Если удачно запиали, закрываем канал.
        end;

      Исходник клиента - так же можно найти в аттаче!
      Всё. Теперь вы знаете, как передовать кучу переменных своим программам из других!

      Автор: Стас "Hexorg" Пономарёв. Хочу поблагодарить www.sources.ru www.delphimasters.ru а также Alex-Eraser
      Сообщение отредактировано: Hexorg -

      Прикреплённый файлПрикреплённый файлPipes.zip (5.49 Кбайт, скачиваний: 434)
      0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
      0 пользователей:


      Рейтинг@Mail.ru
      [ Script execution time: 0,0210 ]   [ 16 queries used ]   [ Generated: 20.04.24, 03:56 GMT ]