I am able to read messages sent over NamedPipeClientStreams by 2 threads using the following code:
Clients:
private static async void DoLogging(string context)
{
try
{
var pipeClient = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
for (var i = 0; i < 10; i++)
{
if (!pipeClient.IsConnected)
pipeClient.Connect(1000);
var buffer = Encoding.UTF8.GetBytes($"Context {context}: Message {i}");
await pipeClient.WriteAsync(buffer, 0, buffer.Length);
pipeClient.Flush();
Thread.Sleep(100); // For testing purposes
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Server:
private static void ListenForConnection()
{
while (true)
{
var pipeServer = new NamedPipeServerStream(PipeName, PipeDirection.InOut,
NamedPipeServerStream.MaxAllowedServerInstances,
PipeTransmissionMode.Message, PipeOptions.Asynchronous);
var namedPipeListener = pipeServer.WaitForConnectionAsync(CancellationToken.None).ToObservable();
namedPipeListener.SubscribeOn(Scheduler.Default).Subscribe(unit =>
{
HandleClient(pipeServer);
});
}
}
private static void HandleClient(PipeStream pipeServer)
{
var bytes = new byte[BufferSize];
var sb = new StringBuilder();
while (true)
{
int numBytes;
do
{
numBytes = pipeServer.Read(bytes, 0, BufferSize);
if (numBytes > 0)
{
sb.Append(Encoding.UTF8.GetString(bytes, 0, numBytes));
}
} while (numBytes > 0 && !pipeServer.IsMessageComplete);
var text = sb.ToString();
if (!string.IsNullOrWhiteSpace(text))
{
Console.WriteLine(text.Trim());
sb.Clear();
}
}
}
The above is called like this:
private const string PipeName = "SvTest.Pipe.Name";
private const int BufferSize = 1024;
private static void Main()
{
try
{
var tasks = new Task[3];
tasks[0] = Task.Run(() => ListenForConnection());
tasks[1] = Task.Run(() => DoLogging("Thread 1"));
tasks[2] = Task.Run(() => DoLogging("Thread 2"));
Task.WaitAll(tasks);
}
catch (Exception ex)
{
LogException(ex);
}
Console.ReadLine();
}
If I try to read the stream using RX I can read the first message an then I get a "Pipe is broken" error in the client threads when the HandleClient method looks like this:
private static void HandleClient(Stream pipeStream)
{
var buffer = new byte[pipeStream.Length];
var sb = new StringBuilder();
var readObservable = pipeStream.ReadAsync(buffer, 0, buffer.Length).ToObservable();
readObservable.SubscribeOn(Scheduler.Default).Subscribe(
unit =>
{
Read(buffer, sb);
},
OnError,
() => { ReadCompleted(sb); }, CancellationToken.None);
}
I assume the server stream is closed after it is read. I am not sure how to keep the stream open and reading from it. The code samples and documentation I was able to find make use of old APM techniques. I want to use the latest/recommended techniques offered by .Net 4.6 and the RX library. In my application the clients will be sending messages at will and over a long period of time. So I want the pipe to remain open per client so as not to incur the connection overhead with every message.
I would also appreciate any guidance on the edge cases so that I end up with a rock solid solution.