Example of Named Pipes

Example of Named Pipes

using System;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
StartServer();
Task.Delay(1000).Wait();


//Client
var client = new NamedPipeClientStream("PipesOfPiece");
client.Connect();
StreamReader reader = new StreamReader(client);
StreamWriter writer = new StreamWriter(client);

while (true)
{
string input = Console.ReadLine();
if (String.IsNullOrEmpty(input)) break;
writer.WriteLine(input);
writer.Flush();
Console.WriteLine(reader.ReadLine());
}
}

static void StartServer()
{
Task.Factory.StartNew(() =>
{
var server = new NamedPipeServerStream("PipesOfPiece");
server.WaitForConnection();
StreamReader reader = new StreamReader(server);
StreamWriter writer = new StreamWriter(server);
while (true)
{
var line = reader.ReadLine();
writer.WriteLine(String.Join("", line.Reverse()));
writer.Flush();
}
});
}
}
}

WCF named pipe minimal example

I just found this excellent little tutorial. broken link (Cached version)

I also followed Microsoft's tutorial which is nice, but I only needed pipes as well.

As you can see, you don't need configuration files and all that messy stuff.

By the way, he uses both HTTP and pipes. Just remove all code lines related to HTTP, and you'll get a pure pipe example.

Real world examples of UNIX named pipes

They are still used, although you might not notice. As a first-class file-system object provided by the operating system, a named pipe can be used by any program, regardless of what language it is written in, that can read and write to the file system for interprocess communication.

Specific to bash (and other shells), process substitution can be implemented using named pipes, and on some platforms that may be how it actually is implemented. The following

command < <( some_other_command )

is roughly identical to

mkfifo named_pipe
some_other_command > named_pipe &
command < named_pipe

and so is useful for things like POSIX-compliant shell code, which does not recognize process substitution.


And it works in the other direction: command > >( some_other_command ) is

mkfifo named_pipe
some_other_command < named_pipe &
command > named_pipe

Named pipe client (c++) with c# server

I know why.

  1. When creating your pipe in C#, use just "TestPipe" as the name of the pipe, don't include \\.\pipe\ as a prefix for this pipe's name.

  2. From C++, use the full path to the pipe: "\\.\pipe\TestPipe". Your C++ logic doesn't need a change for this, as you have defined it just fine: L"\\\\.\\pipe\\TestPipe"

This may help, as well. Funny enough, I ran into this three years ago or more, and now its all coming back to me.

named pipe stream example not showing the result

The client doesn't receive any message from the server, because namedPipeClient.IsMessageComplete must be called after read operation. See PipeStream.IsMessageComplete on docs:

Gets a value indicating whether there is more data in the message
returned from the most recent read operation.

Otherwise namedPipeClient.IsMessageComplete returns true and code inside while-loop is not executed. So you have to rewrite while loop to do-while loop to ensure, that read operation occurs before namedPipeClient.IsMessageComplete is tested.

But there are more problems, see comments for explanations:

using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "test-pipe", PipeDirection.InOut))
{
namedPipeClient.Connect();
namedPipeClient.ReadMode = PipeTransmissionMode.Message;

// StringBuilder is more efficient for string concatenation
StringBuilder serverResponse = new StringBuilder();

byte[] readBytes = new byte[5];

do
{
// You need to store number of bytes read from pipe (to readCount variable).
// It can be less then the length of readBytes buffer, in which case
// GetString() would decode characters beyond end of message.
var readCount = namedPipeClient.Read(readBytes, 0, readBytes.Length);
var readText = Encoding.Default.GetString(readBytes, 0, readCount);

// You original code "overwrites" content of serverResponse variable instead
// of concatenating it to the previous value. So you would receive only
// the last part of the server message.
serverResponse.Append(readText);

// It is not needed to create new buffer, you can just reuse existing buffer
//readBytes = new byte[5];

// IsMessageComplete is now tested after read operation
} while (!namedPipeClient.IsMessageComplete);

System.Console.WriteLine(serverResponse.ToString());

// You current server implementation exits as soon as it sends message to the client
// and does not wait for incomming message. You'll have to change server accordingly
// to be able to send a message back to the server.
//byte[] writeBytes = Encoding.Default.GetBytes("Hello from client!\n");
//namedPipeClient.Write(writeBytes, 0, writeBytes.Length);
}



EDIT:

When named pipe is in the PipeTransmissionMode.Message mode, every call of NamedPipeServerStream.Write() on the server sends data through the pipe as an individual message. Client can then receive these messages separated from each other
(as opposed to PipeTransmissionMode.Byte mode, where client receives just single continuous stream of bytes, no matter how many writes server performed using NamedPipeServerStream.Write()).

When client reads data from pipe (namedPipeClient.Read()), method is allowed to return less data then requested (for example when there is not enough space in the receiving buffer to store the whole message, or message is shorter then the requested number of bytes), see docs.

Returns the total number of bytes that are read into buffer. This
might be less than the number of bytes requested
if that number of
bytes is not currently available, or 0 if the end of the stream is
reached.

You can then use namedPipeClient.IsMessageComplete and readCount to detect this. Let me explain it on some example: Imagine that server sends message ABCDEFGHIJKL to the client, encoded to byte array as { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76 }. This message has length of 12 bytes, thus it does not fit to your receiving buffer (readBytes) which is 5 bytes long. So when client reads from pipe for the first time using namedPipeClient.Read(), receiving buffer will contain just first 5 bytes ({ 65, 66, 67, 68, 69 } corresponding to ABCDE) of the message. And this is where namedPipeClient.IsMessageComplete will help us, because it will return false indicating that we didn't receive complete message, there are still some bytes left and we should continue reading.

The second read from the pipe will be similar, we will read second part of the message ({ 70, 71, 72, 73, 74 } corresponding to FGHIJ), namedPipeClient.IsMessageComplete is still false indicating incomplete message.

When third read from the pipe comletes, only 2 remaining bytes ({ 75, 76 } corresponding to KL) will be read, but our buffer is still 5 bytes long, so it will look like this: ({ 75, 76, 72, 73, 74 } corresponding to KLHIJ). Values 72, 73, 74 are still there from previous iteration of the loop. And now it is important to store value returned from namedPipeClient.Read() to the readCount variable. It will contain value 2, indicating that only 2 bytes of the bytesRead buffer are valid and the remaining bytes should be ignored.

duplex operation between two processes using named pipes in c#

The Problem is the using block of the StreamWriter, which will close the underlying Stream (which is your pipe here). If you don't use that block it should work.

You could do the following:

using (var pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.InOut))
using (var streamReader = new StreamReader(pipeServer))
using (var streamWriter = new StreamWriter(pipeServer))
{
// ... Your code ..
}

As Johannes Egger pointed out, the StreamWriter flushes the stream on Dispose(), so the StreamWriter should be disposed first and thus be the inner-most object to dispose.



Related Topics



Leave a reply



Submit