【问题标题】:Example of Named Pipes命名管道示例
【发布时间】:2012-11-28 04:09:48
【问题描述】:

我如何编写一个简单的——它工作所需的最低限度——测试说明如何使用 IPC/命名管道的应用程序?

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

【问题讨论】:

    标签: c# ipc named-pipes


    【解决方案1】:

    Linux dotnet 核心不支持命名管道!

    如果你部署到 Linux,试试 TcpListener

    此 NamedPipe 客户端/服务器代码将一个字节往返传输到服务器。

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

    DotNet Core 2.0 Server 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 客户端控制台应用

    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
    • 不确定是否可以以管理员身份运行?
    【解决方案2】:

    您实际上可以使用命名管道的名称写入命名管道,顺便说一句。

    以管理员身份打开命令 shell 以绕过默认的“访问被拒绝”错误:

    echo Hello > \\.\pipe\PipeName
    

    【讨论】:

      【解决方案3】:

      对于不熟悉 IPC 和命名管道的人,我发现以下 NuGet 包非常有用。

      GitHub: Named Pipe Wrapper for .NET 4.0

      要使用首先安装包:

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

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

      【讨论】:

      • 我不推荐将此 NuGet 包用于生产。我已经实现了它并且它有一些错误,主要是由于无法真正知道何时在管道的另一端完全接收到消息(导致连接断开,或连接过早结束(检查代码github,如果您不信任我,则不会调用“WaitForPipeDrain”),而且即使只有一个正在收听,您也将拥有多个客户端,因为...问题太多了)。很遗憾,因为它真的很容易使用。我不得不用更少的选择从头开始重建一个。
      • 是的,好点,不幸的是,原来的维护者多年来一直没有更新项目,幸运的是,尽管存在许多分支,其中大部分解决了您讨论的问题。
      • @MartinLaukkanen:你好,我打算使用 NamedPipeWrapper,你知道哪个 fork 正在修复这个 bug 吗?谢谢
      • @MartinLaukkanen 我们可以提供修复上述错误的分叉吗?
      • 我不记得我具体使用了哪一个,但我建议查看 fork 网络图上的提交以确定哪一个修复了您所关心的问题:github.com/acdvorak/named-pipe-wrapper/network
      【解决方案4】:
      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();
                      }
                  });
              }
          }
      }
      

      【讨论】:

      • @JordanTrainor 抱歉,它在 .Net 4.5 中。您可以使用Thread.Sleep
      • @Gusdor 我本可以使用一些同步基元。但它会更难阅读。我认为给出一个关于如何使用 NamedPipes 的想法就足够了
      • 如果你有读完管道关闭的问题,查看这个答案:stackoverflow.com/a/895656/941764
      • 如果你使用.NET 4.5,你可以replace Task.Factory.StartNew with Task.Run
      • reader/writer一定要处理掉吗?如果是这样,您是否只处理其中之一?我从未见过将两者都附加到同一流的示例。
      猜你喜欢
      • 2023-04-06
      • 1970-01-01
      • 2011-05-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-18
      • 1970-01-01
      • 2011-03-05
      相关资源
      最近更新 更多