命名管道示例


130

我如何编写一个简单的(最低工作所需的裸机)测试应用程序,以说明如何使用IPC /命名管道?

例如,如何编写一个控制台应用程序,其中程序1向程序2说“ Hello World”,程序2接收消息并向程序1回复“ Roger That”。

Answers:


165
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();
                }
            });
        }
    }
}

1
@JordanTrainor对不起,它在.Net 4.5中。您可以使用Thread.Sleep

2
@Gusdor我本可以使用一些sync-primiteves。但这将更加难以阅读。我认为足以给出有关如何使用NamedPipes
LB

2
如果您有读取一遍后管道关闭的问题,请检查以下答案:stackoverflow.com/a/895656/941764
jgillich 2013年

11
如果您使用的是.NET 4.5,则可以替换Task.Factory.StartNewTask.Run
Rudey

2
您必须处置reader/ writer吗?如果是这样,您是否只处置其中之一?我从未见过将两者都附加到同一流的示例。
JoshVarty 2015年

20

对于IPC和命名管道的新手来说,我发现以下NuGet软件包会提供很大的帮助。

GitHub:命名为.NET 4.0的Pipe Wrapper

要使用首先安装软件包:

PS> Install-Package NamedPipeWrapper

然后是一个示例服务器(从链接复制):

var server = new NamedPipeServer<SomeClass>("MyServerPipe");
server.ClientConnected += delegate(NamedPipeConnection<SomeClass> conn)
    {
        Console.WriteLine("Client {0} is now connected!", conn.Id);
        conn.PushMessage(new SomeClass { Text: "Welcome!" });
    };

server.ClientMessage += delegate(NamedPipeConnection<SomeClass> conn, SomeClass message)
    {
        Console.WriteLine("Client {0} says: {1}", conn.Id, message.Text);
    };

server.Start();

客户端示例:

var client = new NamedPipeClient<SomeClass>("MyServerPipe");
client.ServerMessage += delegate(NamedPipeConnection<SomeClass> conn, SomeClass message)
    {
        Console.WriteLine("Server says: {0}", message.Text);
    };

client.Start();

对我来说最好的是,与此处接受的答案不同,它支持多个客户端与单个服务器通信。


5
我不建议将此NuGet软件包用于生产。我已经实现了它,并且存在一些错误,主要是由于无法真正知道何时在管道的另一端完全收到了一条消息(导致连接断开,或者连接结束得太早(请检查代码)。如果您不信任我的github,“ WaitForPipeDrain”在应有的情况下不会被调用),而且即使只有一个正在侦听,您也会拥有多个客户端,因为……问题太多了。很难过,因为它真的很容易使用。我不得不用更少的选择从头开始重建一个。
米卡埃尔费利克斯·

是的,很高兴,很幸运的是,原始维护人员多年没有更新过该项目,幸运的是,尽管存在许多分支,但大多数分支都可以解决您所讨论的问题。
Martin Laukkanen

2
@MartinLaukkanen:您好,我打算使用NamedPipeWrapper,您知道哪个叉子修复了此错误吗?谢谢
Whiletrue

16

实际上,您可以使用名称btw来写入命名管道。

以管理员身份打开命令外壳,以解决默认的“访问被拒绝”错误:

echo Hello > \\.\pipe\PipeName

3

Linux上的FYI dotnet核心不支持命名管道,如果您使用的是Linux,请尝试使用tcplistener

此代码有一个客户端往返字节。

  • 客户端写入字节
  • 服务器读取字节
  • 服务器写入字节
  • 客户端读取字节

DotNet Core 2.0服务器ConsoleApp

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

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            var server = new NamedPipeServerStream("A", PipeDirection.InOut);
            server.WaitForConnection();

            for (int i =0; i < 10000; i++)
            {
                var b = new byte[1];
                server.Read(b, 0, 1); 
                Console.WriteLine("Read Byte:" + b[0]);
                server.Write(b, 0, 1);
            }
        }
    }
}

DotNet Core 2.0客户端ConsoleApp

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

namespace Client
{
    class Program
    {
        public static int threadcounter = 1;
        public static NamedPipeClientStream client;

        static void Main(string[] args)
        {
            client = new NamedPipeClientStream(".", "A", PipeDirection.InOut, PipeOptions.Asynchronous);
            client.Connect();

            var t1 = new System.Threading.Thread(StartSend);
            var t2 = new System.Threading.Thread(StartSend);

            t1.Start();
            t2.Start(); 
        }

        public static void StartSend()
        {
            int thisThread = threadcounter;
            threadcounter++;

            StartReadingAsync(client);

            for (int i = 0; i < 10000; i++)
            {
                var buf = new byte[1];
                buf[0] = (byte)i;
                client.WriteAsync(buf, 0, 1);

                Console.WriteLine($@"Thread{thisThread} Wrote: {buf[0]}");
            }
        }

        public static async Task StartReadingAsync(NamedPipeClientStream pipe)
        {
            var bufferLength = 1; 
            byte[] pBuffer = new byte[bufferLength];

            await pipe.ReadAsync(pBuffer, 0, bufferLength).ContinueWith(async c =>
            {
                Console.WriteLine($@"read data {pBuffer[0]}");
                await StartReadingAsync(pipe); // read the next data <-- 
            });
        }
    }
}

像这样将命名管道用于2个进程使我System Unauthorized Accesss Exception - path is denied
Bercovici Adrian

不确定是否可以以管理员身份运行?
帕特里克
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.