NamedPipeServerStream / асинхронные проблемы с надежным отключением - PullRequest
0 голосов
/ 08 августа 2009

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

     m_pipeServer = new NamedPipeServerStream ("Cyber_Srv_EventPipe",
                                             PipeDirection.InOut,
                                             1,
                                             PipeTransmissionMode.Message,
                                             PipeOptions.Asynchronous,
                                             4096,
                                             4096,
                                             pipeSa);
     m_OutputQueue = new List<My_Message>();

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

     do
     {
        if (!m_bClientAttached)
        {
           try
           {
              m_pipeServer.WaitForConnection ();
              m_bClientAttached = true;
           }
           catch (InvalidOperationException invope)
           {
              sDebug = string.Format ("Pipe wait exception InvOpEx: {0}",
                                      invope.Message);
              DebugMessage (sDebug);
           }
        }

        // the message-pumping part of the loop. 

        if (m_bClientAttached)
        {
           try
           {
              if (!m_bReadInProgress)
              {
                 m_bReadInProgress = true;
                 m_pipeServer.BeginRead (byNewRead, 0, byNewRead.GetLength (0),
                                       new AsyncCallback (this.PipeReadComplete),
                                       m_iReadCount);
              }

              if (m_OutputQueue.Count () > 0)
              {
                 if (!m_bWriteInProgress)
                 {
                    m_bWriteInProgress = true;
                    My_Message opmsg = m_OutputQueue.ElementAt (0);
                    m_pipeServer.BeginWrite (opmsg.ToByteArray (), 0,
                                             (int)(opmsg.MsgLength),
                                             new AsyncCallback (this.PipeWriteComplete),
                                             m_iWriteCount);
                 }
              }
           }
           catch (IOException ioe)
           {
              sDebug = string.Format ("Main loop raised exception: {1}",
                                      ioe.Message);
              DebugMessage (sDebug);
              DetachClientPipe();
           }
           Thread.Sleep(1);
        }

     } while (m_bRunning);

     m_pipeServer.Close ();
  }

Процедуры завершения чтения и записи выглядят так:

  private void PipeReadComplete (IAsyncResult iAR)
  {
     string sDebug;
     int iByteCount;
     My_Message ipmsg = new My_Message();
     try
     {
        iByteCount = m_pipeServer.EndRead (iAR);
        if (iByteCount > 0) 
        {
           ipmsg.FromByteArray(byNewRead);
           m_bReadInProgress = false;
           ... process message ...
        }
        else
        {
           try
           {
              DebugMessage ("PRC: Zero bytes read, disconnect pipe");
              DetachClientPipe();
           }
           catch (InvalidOperationException invope)
           {
              sDebug = string.Format ("PRC - Pipe disconnect exception: {0}",
                                      invope.Message);
              DebugMessage (sDebug);
           }
        }
     }
     catch (IOException e)
     {
        sDebug = string.Format ("PRC: Read {0} raised exception: {1}",
                                (int)(iAR.AsyncState),
                                e.Message);
        DebugMessage (sDebug);
        DetachClientPipe();
     }
  }

  // ------------------------------------------------------------------

  private void PipeWriteComplete (IAsyncResult iAR)
  {
     string sDebug;
     try
     {
        m_pipeServer.EndWrite (iAR);
        lock (m_OutputQueue)
        {
           m_OutputQueue.RemoveAt(0);
        }
        m_bWriteInProgress = false;
     }
     catch (IOException e)
     {
        sDebug = string.Format ("Write {0} raised exception: {1}",
                                (int)(iAR.AsyncState),
                                e.Message);
        DebugMessage (sDebug);
        DetachClientPipe();
     }
  }

  // ------------------------------------------------------------------

  private void DetachClientPipe()
  {
     if (m_pipeServer.IsConnected)
     {
        m_pipeServer.Disconnect();
     }
     m_bClientAttached = false;
  }

Код на стороне клиента известен как хороший код, используется повторно. Так вот в чем проблема. Клиент может подключиться нормально. Затем мы закрыли клиента, все в порядке. Мы запускаем и снова подключаемся. Все хорошо, затем мы закрываем и запускаем снова. Бум - ошибка 231, труба занята. сервер теперь будет генерировать ошибки занятости канала при любой попытке подключения, пока ад не замерзнет, ​​или мы не перезапустим службу. Тогда мы снова вернемся к двум соединениям.

Я смотрю на этот код уже три дня подряд и понятия не имею, почему он это делает. Кажется, я не вижу дрова для деревьев, и я мог бы использовать свежую пару глаз или три. Проблема в том, что никто в команде не знает много о C #.

Обновление

Причина, по которой это не удается при третьей попытке подключения, заключается в том, что при первом отключении возвращается PipeReadComplete, и я получаю нулевое чтение байтов, поэтому я отсоединяю канал, и все в порядке. НО ... при втором отключении PipeReadComplete НЕ вызывается, поэтому я не принудительно отключаю. Weird.

Ответы [ 2 ]

1 голос
/ 03 октября 2011

Боб, для быстрого исправления: было интересно, вы пытались установить параметр экземпляра сервера более чем на 1 и посмотреть, если он все еще не работает после 2 попыток? Вместо 1 поставьте 10 и посмотрите, поможет ли это. Кроме того, это поможет, если вы также разместите неуправляемый код. В настоящее время я делаю то же самое, служба Windows плюс неуправляемый dll IPC.

    m_pipeServer = new NamedPipeServerStream  ("Cyber_Srv_EventPipe",    
                                         PipeDirection.InOut,              
                                         10,
                                         PipeTransmissionMode.Message,  
                                         PipeOptions.Asynchronous,                                                 
                                         4096,
                                         4096,
                                         pipeSa);       

Или вам действительно нужен только один экземпляр канала сервера?

0 голосов
/ 08 августа 2009

Пожалуйста, см. этот связанный вопрос для возможного ответа. Похоже, Сума испытал и решил ту же проблему, и, хотя не в C #, его должно быть довольно легко перевести.

...