7

I have a client using NamedPipeClientStream and a server using NamedPipeServerStream.

The client may start before the server, and when it call clientStream.Connect(timeout) i get TimeoutException as expected.

Is there any way I can check if there is a NamedPipeServerStream listening before calling the Connect to prevent an exception?

Joseph
  • 1,716
  • 3
  • 24
  • 42

3 Answers3

10

If someone bumps into this question five years later, this might be helpful:

var isPipeRunning = Directory.GetFiles( @"\\.\pipe\" ).Contains( $"\\.\pipe\{pipeName}" )
Csaba Fabian
  • 846
  • 9
  • 19
  • Thanks man, very simple and straight forward way to check. – FFMG Jan 26 '21 at 17:23
  • 1
    This also seems to work: `System.IO.File.Exists( @"\\.\pipe\{pipename}" )` – Mike Nakis Aug 09 '21 at 11:43
  • Just to add to @MikeNakis comment - it's a neater solution - but actually called the pipe in my test harness, so check this isn't going to interfere with your comms. Getting the pipe directory list then looking for the pipe name didn't. I'm using System.IO.Abrstractions, so YMMV – Keith Paul Barrow Dec 24 '21 at 12:19
  • File.Exists( @"\\.\pipe\pipename" ) will cause the pipe broken. https://github.com/dotnet/runtime/issues/69604 Using Directory.GetFiles( @"\\.\pipe\" ) .Contains( $"\\.\pipe\{pipeName}" ) is working. – Shangwu Oct 27 '22 at 20:15
  • A little modification is needed for this to work under my environment, maybe it'll help others: Directory.GetFiles(@"\\.\pipe\").ToList().Contains($@"\\.\pipe\{ControlPipeName}") – Ben Lugasi Dec 06 '22 at 09:40
3

I suggest you should use an EventWaitHandle. On all clients, call WaitOne () and on server after opening the stream, call Set ().

So, on "server" side, write this:

EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset, String.Format(@"Global\{0}", "SERVER_OPENED_HANDLE"));

OpenStream (); // inside this method you would have code that opens your named pipe for incomming messages

// finally, signal that you are done

handle.Set ();

On client side, write something like this:

EventWaitHandle handle = new EventWaitHandle(false, EventResetMode.ManualReset, String.Format(@"Global\{0}", "SERVER_OPENED_HANDLE"));

// here your thread will sleep until the server calls "Set"
handle.WaitOne ();

// then you can safelly connect to the server here

ConnectToServer ();

There remains just a few situations to handle:

1) The pipe can't be opened on server becase there is already an opened pipe with the same name (will throw an exception).

2) You successfully opened the pipe, you notified clients that you are ready but after that, a milisecond after, the server crushes from some unexpected reason and the client cannot reach the server.

3) User rights problems

In all these situations, you should handle these exceptions using a try / catch and normally, if all goes well, this code will ensure that the client will NOT try to connect before the server successfully opened the pipe.

Anyway, I suggest using a more advanced technique for making IPC through Named Pipes such as using WCF or even .NET Remoting wich besides the fact that it's somehow considered obsolete by many (don't include me) is very decent at least for IPC communication. This will give you the freedom and scalability (maybe one day you will need your clients to be able to reside on other machines, and you will have to switch from IPC to a LAN communication or even in WAN / internet).

George Lica
  • 1,798
  • 1
  • 12
  • 23
  • `EventWaitHandles` does not work across process boundarys. – vidstige Aug 06 '15 at 06:40
  • Are you very very sure? – George Lica Aug 06 '15 at 06:42
  • @vidstige it possible to do this, here an example for Creating a Cross-Process EventWaitHandle: http://stackoverflow.com/questions/2590334/creating-a-cross-process-eventwaithandle – Joseph Aug 06 '15 at 06:47
  • @GeorgeLica: thanks, I think it make science, to wait for signal.... BUT, what would happen if the server started before the client been created?, the signal been set before client registered to that signal (before create the client instance at all)???!! – Joseph Aug 06 '15 at 06:53
  • @Joseph I don't see a problem, you are the server your job is to wait for incomming calls. You open the pipe, you send the "i am ready" signal and you just wait. The client will call eventually later "waitone" but he will not wait since the eventwaithandle is found already in "Set" mode – George Lica Aug 06 '15 at 14:55
  • "he will not wait since the eventwaithandle is found already in "Set" mode" : this exactly what I wanted to verify, thanks!!! – Joseph Aug 07 '15 at 07:18
2

There is no way to check this only using a NamedPipeClientStream. However, you can use a Mutex like so

// In the server
var mutex = new System.Threading.Mutex(false, "MyPipeMutex");
OpenPipeAndRunServer();
mutex.Close();

// In the client process    
var mutex = new System.Threading.Mutex(false, "MyPipeMutex");
if (!mutex.WaitOne(0, false))
{
    OpenPipe();
}
mutex.Close();

You will probably want to wrap the Close calls in a try-finally block to make sure it always closes. In the client you can use a different timeout to actually wait for the NamedPipe to be opened.

You can also catch the exception as a work around.

vidstige
  • 12,492
  • 9
  • 66
  • 110