Мы используем именованные каналы для связи между службой 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.