Named pipe server unable to cleanly recover from clients that disconnect

  • Thread starter Thread starter BetterCheef
  • Start date Start date
B

BetterCheef

Guest

Hello

I had posted this same message on old thread that was started with Matt Kelner. I am restricted from providing its URL and hence I am mentioning the link as broken text


https colon slash slash social dot msdn dot Microsoft dot com slash Forums slash en-US slash 7bdcf692-ed99-447d-9b90-586b1e027633 slash how-do-i-detect-a-client-disconnected-from-a-named-pipe?forum=csharpgeneral

Fearing that the post may go unmonitored for a while, I am posting it here with little editing.

From whatever I have read on net, I feel that there are deep-seated problems in pipe implementation Windows API seem to be still unresolved. I say this because I too am facing same problem with the NamedPipeServerStream class. I am using it in real time application and cycling through all of the errors that are (other-wise) documented.

Newer wrappers are being added on top of it with sketchy documentation that is extremely subjective and each variant of a wrapper seems to omitting some critical method. Net result is that army of frustrated developers have uploaded ton of varying implementation of code (all erroneous) and that inundates the cyber space making it fun for google/bing but making it more and more difficult for unaware to separate "the needle from the hay".

I have myself download close to 80 different pipe server samples but none of them actually work well in all test scenarios. I have to say that the server implementation uploaded by Matt Kellner in above referenced thread as being one of the best and most educative.

Anyways, here is summary of my problem that I am experiencing in 2018.

As mentioned above, I am using NamedPipe in a real time application and cycling through all of the errors that are (other-wise) documented. Client is written in C++ (non MFC, no WinRT) and server is written in C#.NET 4.5.1

When I use the named pipe server in synchronous mode, it creates a tiny black hole zones on the time-line during which no listening server is available for a pipe client to send information to.

No delay can introduced in client side and caching message in event of send-error is impossible. If server is not ready to receive the message, client simply moves on. The black-zone time span is about 3-4 mili secs but that is ample for messages from fast moving pipe client to slip and fall through. Our QA team reported this bug with a statement that "For a pipe client that sends messages too fast, every alternate messages get lost in transit". Not only was I able to see this problem manifest itself in the log sent to me by QA team, I was also able to recreate it on my machine.

When I implement the named pipe server in asynchronous mode, the server sometimes fail to reset and that error manifest with observation neither BeginWaitForConnection nor WaitForConnection actually do the blocking. This causes the listening server thread to keep spinning endlessly returning back with zero bytes read (all originated from a pipe client that has already disconnected and in state of being disposed but yet to be cleaned off).

This is what my server implementation looks like

class PipeReader
{
public const int BUFFER_SIZE = 1000;

private const String sRecvPipeName = "OurSpecialPipe"; // Note: Name on C# needs be without prefix of \\\\.\\pipe\\

private Thread listenThread = null;

private bool connectedOrWaiting = false;

private NamedPipeServerStream myPipeServer = null;

// A flag that controls the life-span of the loop inside the Thread
private bool running = false;

public void StartTheListenerASynchronous()
{
// Without this lock statement, a double click from user on toolbar or button can unleash havoc on pipe construction code
lock (this)
{
if (!running)
{
running = true;

listenThread = new Thread(new ThreadStart(listenForClientsASynchronous));
listenThread.Name = "PipeReaderThread";
listenThread.Start();

//PerformLoggingAtTheStartOfServer(...);
}
Monitor.PulseAll(this);
}
}

private void listenForClientsASynchronous()
{
myPipeServer = new NamedPipeServerStream(
sRecvPipeName, PipeDirection.In, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte,
PipeOptions.Asynchronous, BUFFER_SIZE, BUFFER_SIZE);

byte[] dataBuffer = new byte[BUFFER_SIZE];

while (running)
{
try
{
lock (myPipeServer)
{
if (!connectedOrWaiting)
{
myPipeServer.BeginWaitForConnection((a) =>
{ if (myPipeServer != null) myPipeServer.EndWaitForConnection(a); }, null);

connectedOrWaiting = true;
}

if (myPipeServer.IsConnected)
{
myPipeServer.Read(dataBuffer, 0, BUFFER_SIZE);

ProcessClientMessage(encoder.GetString(dataBuffer, 0, dataBuffer.Length));

myPipeServer.WaitForPipeDrain();

myPipeServer.Disconnect();

connectedOrWaiting = false;
}

Monitor.PulseAll(myPipeServer);
}
}
catch (ThreadInterruptedException tix)
{
ExceptionHandler.LogThisException(tix, "Pipereader thread interrupted");
}
catch (ThreadAbortException tax)
{
ExceptionHandler.LogThisException(tax, "Pipereader thread aborted");
}
catch (Exception ex)
{
ExceptionHandler.LogThisException(ex);
}
finally
{
}
} // while running loop

try
{
myPipeServer.Close();
myPipeServer = null;
}
catch(Exception)
{
}
}
}



Continue reading...
 
Back
Top