There is a race condition in your server between the ConnectionManager
loop that creates the named pipe instance, and the Receiver
thread that closes the handle. As soon as the Receiver closes the instance handle, it will cease to be visible to the client until the next time CreateNamedPipe
is called. This will result in the ERROR_FILE_NOT_FOUND error.
I think there are two potential solutions, depending on whether you want to be able to serve more than one client at a time.
To Serve A Single Client At a Time
If you know that in your application you only need to serve one client at a time, then there is really no need for a separate Receiver
thread. Just create the named pipe instance one time with CreateNamedPipe
, handle the client request inline when ConnectNamedPipe
returns, and then call DisconnectNamedPipe
when the client request is done (instead of closing the named pipe instance handle). If you call DisconnectNamedPipe
, then you can just call ConnectNamedPipe
with the same instance handle and all is good.
private void ConnectionManager()
{
_pipeHandle = CreateNamedPipe(_pipeName, INBOUND|FILE_FLAG_OVERLAPPED,
REJECT_REMOTE_CLIENTS|READMODE_BYTE,
1, 0, BUFFER_SIZE, 0, IntPtr.Zero);
while ( NamePipeProcessing )
{
//THIS IS A BLOCKING CALL
int success = ConnectNamedPipe(_pipeHandle , IntPtr.Zero);
// handle the request here with CreateFile(), etc
DisconnectNamedPipe(_pipeHandle);
}
N.B. In this single threaded scenario, the client can receive an ERROR_PIPE_BUSY error when it tries to connect to the pipe. If this happens, just use WaitNamedPipe
, which will wait for the server to get around to calling ConnectNamedPipe
again.
To Serve Multiple Clients on Multiple Threads
Now, if you want to be able to serve multiple threads simultaneously, the simplest solution is to create a new pipe instance before handling each incoming client request. E.g., as soon as ConnectNamedPipe
returns with a new client connection, call CreateNamedPipe
to create a new pipe instance before kicking off the thread to handle the incoming client request. This ensures that you will never have a window during which the server doesn't have an instance available.
// create the first named pipe instance
_pipeHandle = CreateNamedPipe(...)
while ( NamePipeProcessing )
{
// wait for new client connection
ConnectNamedPipe(_pipeHandle , IntPtr.Zero);
// ...error checking omitted
// save off instance handle for use by Receiver thread - it will CloseHandle it
_clientInfo.handle = _pipeHandle;
// create a new named pipe instance before processing this request
_pipeHandle = CreateNamedPipe(...)
// spawn Receiver thread to handle request
_receiveThread = new Thread(new ThreadStart(Receiver));
}