I have two processes ( both .NET Framework
applications) and i am trying to communicate using Named Pipes
The client connects to the pipe , but when it tries to ReadAsync
the message sent by the server, it starts waiting indifinetly , even though the server has already sent a message !!
What surprises me is that if i close the Server
app the Client
finally continues to the next line of code (after the ReadAsync
line), having read 0 bytes.
Is there anything i must to on the server after WriteAsync
-ing the payload? Do i need to flush or anything ?
Server
static async Task Main(string[] args) {
var server = new NamedPipeServerStream(
"somePipe",
PipeDirection.InOut,
2,
PipeTransmissionMode.Message
);
await server.WaitForConnectionAsync();
using (StreamReader reader = new StreamReader(server)) {
using (StreamWriter writer = new StreamWriter(server)) {
await writer.WriteAsync("Hello from server");
char[] buffer = new char[20];
var read = await reader.ReadAsync(buffer, 0, buffer.Length);
}
}
}
Client
static async Task Main() {
NamedPipeClientStream client = new NamedPipeClientStream(
".",
"somePipe",
PipeDirection.InOut,
PipeOptions.Asynchronous);
await client.ConnectAsync();
try {
using (StreamReader reader = new StreamReader(client)) {
using (StreamWriter writer = new StreamWriter(client)) {
var buffer = new char[20];
int readChars = await reader.ReadAsync(buffer, 0,buffer.Length); //starts waiting indifinetly , until i close the Server
await writer.WriteAsync("From Client");
}
}
} catch (Exception ex) {
throw;
}
}
Update
It seems using the NamedPipeServerStream
directly to write instead of the StreamWriter
on the server side makes the client get the data !
Server Changes
byte[] data=Encoding.UTF8.GetBytes("Hello from server");
server.WriteAsync(data,0,data.Length);
P.S
However using again the server
to ReadAsync
blocks the server.So there is a problem when wrapping the NamedPipeServerStream
and ClientStream
into StreamReader
and StreamWriter
.
This blocking/lock issue is caused by the code that's generated undercovers by the async/await syntactic sugar.
If you don't add ConfigureAwait(false)
, the compiler generates something that's cool for a UI application (UI is a general term, could be ASP.NET - not core - , WPF, Winforms, etc.), but that can be deadly otherwise.
A lot has beew written on this matter:
quoted:
There are two best practices [...] that avoid this situation:
- In your “library” async methods, use ConfigureAwait(false) wherever possible.
- Don’t block on Tasks; use async all the way down.
Parallel Computing - It's All About the SynchronizationContext
Best practice to call ConfigureAwait for all server-side code
Should I call ConfigureAwait(false) on every awaited operation
So, the simplest solution is to add ConfigureAwait(false)
everywhere in your code that is not known to run in the UI, or use async
everywhere (never block on any task ever in your whole code, really everywhere, including in code you don't own).
IMHO, this is ridiculous... I personally use a Visual Studio extension that yells at me, if I forget to add it behind every async
call :-)
Note there are some alternatives, for example: An alternative to ConfigureAwait(false) everywhere