Программирование для Windows NT (том 2)

       

Приложение PIPES


Исходные тексты приложения PIPES приведены в листинге 2.11.

Листинг 2.11. Файл pipe/pipes/pipes.c

// ==================================================

// Приложение PIPES (серверное приложение)

// Демонстрация использования каналов Pipe

// для передачи данных между процессами

//

// (С) Фролов А.В., 1996

// Email: frolov@glas.apc.org

// ==================================================

#include <windows.h>

#include <stdio.h>

#include <conio.h>

int main()

{

  // Флаг успешного создания канала



  BOOL   fConnected;

  // Идентификатор канала Pipe

  HANDLE hNamedPipe;

  // Имя создаваемого канала Pipe

  LPSTR  lpszPipeName = "\\\\.\\pipe\\$MyPipe$";

  // Буфер для передачи данных через канал

  char   szBuf[512];

  // Количество байт данных, принятых через канал

  DWORD  cbRead;

  // Количество байт данных, переданных через канал

  DWORD  cbWritten;

  printf("Named pipe server demo\n"

    "(C) A. Frolov, 1996, Email: frolov@glas.apc.org\n");

  // Создаем канал Pipe, имеющий имя lpszPipeName

  hNamedPipe = CreateNamedPipe(

    lpszPipeName,

    PIPE_ACCESS_DUPLEX,

    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,

    PIPE_UNLIMITED_INSTANCES,

    512, 512, 5000, NULL);

   

  // Если возникла ошибка, выводим ее код и зваершаем

  // работу приложения

  if(hNamedPipe == INVALID_HANDLE_VALUE)

  {

    fprintf(stdout,"CreateNamedPipe: Error %ld\n",

      GetLastError());

    getch();

    return 0;

  }

  // Выводим сообщение о начале процесса создания канала

  fprintf(stdout,"Waiting for connect...\n");

  // Ожидаем соединения со стороны клиента

  fConnected = ConnectNamedPipe(hNamedPipe, NULL);

 

  // При возникновении ошибки выводим ее код

  if(!fConnected)

  {

    switch(GetLastError())

    {

      case ERROR_NO_DATA:

        fprintf(stdout,"ConnectNamedPipe: ERROR_NO_DATA");


        getch();

        CloseHandle(hNamedPipe);

        return 0;

      break;

      case ERROR_PIPE_CONNECTED:

        fprintf(stdout,

          "ConnectNamedPipe: ERROR_PIPE_CONNECTED");

        getch();

        CloseHandle(hNamedPipe);

        return 0;

      break;

      case ERROR_PIPE_LISTENING:

        fprintf(stdout,

          "ConnectNamedPipe: ERROR_PIPE_LISTENING");

        getch();

        CloseHandle(hNamedPipe);

        return 0;

      break;

      case ERROR_CALL_NOT_IMPLEMENTED:

        fprintf(stdout,

           "ConnectNamedPipe: ERROR_CALL_NOT_IMPLEMENTED");

        getch();

        CloseHandle(hNamedPipe);

        return 0;

      break;

      default:

        fprintf(stdout,"ConnectNamedPipe: Error %ld\n",

          GetLastError());

        getch();

        CloseHandle(hNamedPipe);

        return 0;

      break;

    }

    CloseHandle(hNamedPipe);

    getch();

    return 0;

  }

  // Выводим сообщение об успешном создании канала

  fprintf(stdout,"\nConnected. Waiting for command...\n");

  // Цикл получения команд через канал

  while(1)

  {

    // Получаем очередную команду через канал Pipe

    if(ReadFile(hNamedPipe, szBuf, 512, &cbRead, NULL))

    {

      // Посылаем эту команду обратно клиентскому

      // приложению

      if(!WriteFile(hNamedPipe, szBuf, strlen(szBuf) + 1,

        &cbWritten, NULL))

        break;

      // Выводим принятую команду на консоль

      printf("Received: <%s>\n", szBuf);

     

      // Если пришла команда "exit",

      // завершаем работу приложения

      if(!strcmp(szBuf, "exit"))

        break;

    }

    else

    {

      fprintf(stdout,"ReadFile: Error %ld\n",

        GetLastError());

      getch();

      break;

    }

  }

  CloseHandle(hNamedPipe);

  return 0;

}



В области локальных переменных функции main определена строка lpszPipeName, в которой хранится имя канала:

LPSTR  lpszPipeName = "\\\\.\\pipe\\$MyPipe$";

Так как канал создается локально, в качестве имени компьютера указан символ точки. Канал называется $MyPipe$.

Буфер szBuf размером 512 байт нужен для хранения данных, передаваемых через канал.

В переменные cbRead и cbWritten при выполнении операций чтения и записи через канал записывается, соответственно, количество принятых и переданных байт данных.

После вывода “рекламной” строки приложение PIPES создает канал, вызывая для этого функцию CreateNamedPipe:

hNamedPipe = CreateNamedPipe(

    lpszPipeName,

    PIPE_ACCESS_DUPLEX,

    PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,

    PIPE_UNLIMITED_INSTANCES,

    512, 512, 5000, NULL);

В качестве первого параметра мы передаем этой функции имя канала. Во втором канале указана константа PIPE_ACCESS_DUPLEX, поэтому канал работает и на прием информации, и на передачу (в дуплексном режиме).

Константа PIPE_TYPE_MESSAGE определяет, что через канал будут передаваться сообщения заданной длины (а не просто последовательность байт данных).

Если в процессе создания канала произошла ошибка, ее код определяется с помощью функции GetLastError. Вслед за этим в консольное окно приложения выводится сообщение с кодом ошибки и приложение переводится в состояние ожидания до тех пор, пока пользователь не нажмет какую-нибудь клавишу. После этого работа приложения завершается.

После создания канала, который будет работать в блокирующем режиме, вызывается функция ConnectNamedPipe:

fConnected = ConnectNamedPipe(hNamedPipe, NULL);

Из-за блокирующего режима работы и из-за того, что канал работает без перекрытия в синхронном режиме, после вызова функции ConnectNamedPipe сервер перейдет в состояние ожидания. Он будет находиться в этом состоянии до тех пор, пока клиентское приложение PIPEC не установит с ним канал Pipe.

При возникновении ошибки наше приложение получает ее код и анализирует его, выводя в консольное окно соответствующее сообщение. Затем приложение закрывает идентификатор канала и завершает свою работу.



После успешного создания канала приложение PIPES входит в цикл получения команд от клиентского приложения PIPEC:

while(1)

{

  if(ReadFile(hNamedPipe, szBuf, 512, &cbRead, NULL))

  {

    if(!WriteFile(hNamedPipe, szBuf, strlen(szBuf) + 1,

        &cbWritten, NULL))

        break;

    printf("Received: <%s>\n", szBuf);

    if(!strcmp(szBuf, "exit"))

        break;

  }

  else

  {

    fprintf(stdout,"ReadFile: Error %ld\n",

      GetLastError());

    getch();

    break;

  }

}

В этом цикле серверное приложение читает команду из канала, пользуясь для этого обычной функцией ReadFile, которую вы знаете из предыдущей главы нашей книги. Так как канал работает в синхронном блокирующем режиме, функция ReadFile будет находиться в состоянии ожидания до тех пор, пока клиентское приложение не пришлет через канал Pipes сообщение с командой. Принятое сообщение записывается в буфер szBuf, а его размер - в переменную cbRead.

Если сообщение принято успешно, оно тут же посылается обратно, для чего серверное приложение посылает его обратно при помощи функции WriteFile. Так как сообщение представляет собой текстовую строку, закрытую двоичным нулем, при посылке размер сообщения вычисляется как длина этой строки плюс один байт.

Далее серверное приложение сравнивает принятую команду со строкой exit. Если от клиента пришла эта строка, цикл получения команд завершается.


Содержание раздела