Трубная связь C++

Я пишу два небольших приложения на С++, которые должны взаимодействовать. Первым будет сервис, который время от времени должен о чем-то предупреждать пользователя. Поскольку служба не может создавать окна, я разработал приложение как два отдельных исполняемых файла.

Служба будет использовать уведомитель для связи.

Службе нужно только отправлять текстовые сообщения на уведомитель, который покажет всплывающую подсказку в системном трее.

Я пытаюсь использовать именованные каналы и думаю, что почти достиг цели, но не совсем. Что у меня есть до сих пор:

На стороне уведомителя:

  m_hInPipe = CreateNamedPipe(L"\\\\.\\pipe\\nhsupspipe", PIPE_ACCESS_INBOUND,
                PIPE_WAIT, 1, 1024, 1024, 60, NULL);

Это означает, что я создал канал с именем nhsupspipe, входящий канал.

Со стороны сервиса:

if (!WriteFile(m_hOutPipe, "My message to the user?", 23, &escritos, &o))
     std::cout << "ERROR: " << GetLastError();

При отладке вижу, что все ок, пайп создан и WriteFile записывает в пайп мои 23 байта.

Мой вопрос: как на стороне уведомителя я смогу прочитать эти байты? Отправлено ли какое-либо сообщение процессу? Должен ли я писать обработчик для канала? Что-либо?


person Ricardo Acras    schedule 04.12.2009    source источник


Ответы (5)


Несколько простых фрагментов от клиента (вашей службы) и сервера (уведомителя) [Примечание. Это адаптировано из проекта, который я сделал некоторое время назад, на который, в свою очередь, сильно повлияли образцы MSDN из CreateNamedPipe. и др.]:

Сторона сервера:

HANDLE hPipe = INVALID_HANDLE_VALUE;
bool bConnected = false;

 hPipe = CreateNamedPipe( L"\\\\.\\pipe\\nhsupspipe",
                          PIPE_ACCESS_DUPLEX,
                          PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
                          PIPE_UNLIMITED_INSTANCES,
                          sizeof( Message ),
                          0,
                          0,
                          NULL );

 // failed to create pipe?
 if( hPipe == INVALID_HANDLE_VALUE ){
   return -1;
 }

 // Wait for the client to connect; if it succeeds, 
 // the function returns a nonzero value. If the function
 // returns zero, GetLastError returns ERROR_PIPE_CONNECTED. 
 bConnected = ConnectNamedPipe( hPipe, NULL ) ? true : ( GetLastError() == ERROR_PIPE_CONNECTED );

 if( bConnected ){
  while( true ){ 
   unsigned long ulBytesRead = 0;
   // read client requests from the pipe. 
   bool bReadOk = ReadFile( hPipe,
                            &message,
                            sizeof( message ),
                            &ulBytesRead,
                            NULL );

   // bail if read failed [error or client closed connection]
   if( !bReadOk || ulBytesRead == 0 )
    break;

   // all ok, process the message received

  }
 }
 else{
   // the client could not connect, so close the pipe. 
   CloseHandle( hPipe );
 }

 return 0;

Клиент:

HANDLE hPipe = INVALID_HANDLE_VALUE;

 // create the named pipe handle
 hPipe = CreateFile( L"\\\\.\\pipe\\nhsupspipe",
                     GENERIC_READ | GENERIC_WRITE,
                     0, 
                     NULL, 
                     OPEN_EXISTING,
                     0,
                     NULL );

 // if everything ok set mode to message mode
 if( INVALID_HANDLE_VALUE != hPipe ){
  DWORD dwMode = PIPE_READMODE_MESSAGE;
  // if this fails bail out
  if( !SetNamedPipeHandleState( hPipe, &dwMode, NULL, NULL ) ){
   CloseHandle( hPipe ); 

   return -1;
  }
 }

 unsigned long ulBytesWritten = 0;
 bool bWriteOk = WriteFile( hPipe, 
                            ( LPCVOID )&message, 
                            sizeof( Message ), 
                            &ulBytesWritten, 
                            NULL );

 // check if the writing was ok
 if( !bWriteOk || ulBytesWritten < sizeof( Message ) ){
  return -1;
 }

 // written ok

 return 0;

Упомянутое выше сообщение представляет собой структуру, которая будет вашим сообщением, которое вы, вероятно, захотите пакет.

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

И немного по-другому: вам следует внимательно рассмотреть то, что г-н Остерман сказал в своем ответе. (даже ни за что, кроме как за то, что он Ларри Остерман).

person Eugen Constantin Dinca    schedule 05.12.2009
comment
подскажите, пожалуйста, как реализовать сервер и клиент в одном проекте на VS (C++)? - person kushpf; 27.06.2014
comment
@k_programmer Если вы хотите, чтобы оба процесса были в одном процессе, вам придется либо использовать отдельные потоки для сервера и клиента, либо использовать ReadFileEx и WriteFileEx. Попробуйте реализовать решение, и если у вас есть проблемы, спросите здесь. - person Eugen Constantin Dinca; 28.06.2014
comment
Спасибо за ответ. Я нашел способ реализовать сервер и клиент в одном потоке. Кажется странным, но я как-то это сделал, и теперь он работает правильно :) - person kushpf; 28.06.2014
comment
@k_programmer для полудуплексного протокола вам не понадобятся отдельные потоки. В любом случае, приятно знать, что вы это поняли. - person Eugen Constantin Dinca; 28.06.2014

Вам необходимо использовать ReadFile или ReadFileEx (для перекрывающихся операций ввода-вывода) обычно на стороне уведомителя в потоке или цикле сообщений. Также просмотрите документацию для CreateNamedPipe и WaitNamedPipe.

person Ryan    schedule 04.12.2009

В этом случае я бы, вероятно, использовал RPC вместо необработанных именованных каналов. С необработанными именованными каналами вы должны анализировать данные от клиента, что может привести к ошибкам безопасности. С помощью RPC вы можете позволить RPC анализировать данные за вас, что снижает вероятность появления ошибки.

person ReinstateMonica Larry Osterman    schedule 04.12.2009

Не совсем вопрос, но другой вариант - CreateEvent() и файл с отображением памяти.

person kenny    schedule 04.12.2009

Если бы я делал это, я бы заставил сервисную сторону выполнить CreateNamedPipe (исходящий), а затем вызвать ConnectNamedPipe, чтобы дождаться подключения уведомителя.

На стороне уведомителя я бы использовал CreateFile с FILE_FLAG_OVERLAPPED. При этом дескриптор канала будет сигнализироваться, когда данные станут доступны для чтения. Ваш уведомитель (предположительно) также будет поддерживать графический интерфейс, поэтому вы, вероятно, захотите, чтобы он вызывал MsgWaitForMultipleObjects в своем цикле событий. Это будет ожидать обработки сообщений или данных, поступающих в канал для обработки. Это будет работать почти как обычный цикл обработки событий, за исключением того, что его возвращаемое значение немного отличается от GetMessage() — он вернет WAIT_OBJECT_0, если ваш дескриптор получает сигнал, или WAIT_OBJECT_0 + 1, если у вас есть сообщение (при условии, что вы ожидаете только одно дескриптор -- на самом деле это WAIT_OBJECT_0 + N, где N – это количество дескрипторов, которые вы ждали). По большей части это похоже на обычный цикл PeekMessage или GetMessage — подождите, пока не будет что делать, сделайте это и снова подождите.

person Jerry Coffin    schedule 04.12.2009