zoukankan      html  css  js  c++  java
  • 进程间通讯Pipe管道通讯

    一、Pipe管道通讯基本概念

    管道是一种最古老也是最基本的系统IPC形式,主要有两种:匿名管道(普通管道)和命名管道。

    匿名管道(普通管道)

    普通管道允许两个进程按标准的生产者-消费者方式进行通信:生产者向管道的一端(写入端)写,消费者从管道的另一端(读出端)读。

    因此,普通管道是单向的,只允许单向通信。如果需要双向通信,那么就要采用两个管道,而每个管道向不同方向发送数据。通常情况下,父进程创建一个管道,并使用它来与其子进程进行通信。

    命名管道

    命名管道提供了一个更强大的通信工具。通信可以是双向的,并且父子关系不是必需的,当建立了一个有名管道后,多个进程都可用它通信。

    二、命名管道的使用

    例如,我们实现一个应用场景:客户端(进程A)通过管道请求服务端(进程B)后获取到数据。具体实现如下:

    1、我们将客户端与服务端的共有方法抽象到基类PipeBase上:

     public class PipeBase
        {
            protected Encoding Encoding { get; set; } = Encoding.UTF8;
            protected StreamWriter PipeWriter { get; set; }
            protected StreamReader PipeReader { get; set; }
            protected const string Exit = nameof(Exit);
            protected void Send(string input)
            {
                string base64 = Convert.ToBase64String(Encoding.GetBytes(input));
                PipeWriter.WriteLine(base64);
            }
    
            protected string Receive()
            {
                string base64 = PipeReader.ReadLine();
                if (string.IsNullOrEmpty(base64)) return null;
                var bytes = Convert.FromBase64String(base64);
                var output = Encoding.GetString(bytes);
                if (output == Exit)
                {
                    Environment.Exit(0);
                }
                return output;
            }
        }
    

    2、编写Pipe管道客户端PipeClient类(需引入 System.Security.Principal命名空间):

      public class PipeClient: PipeBase
        {
            private NamedPipeClientStream Client { get; set; }
            public PipeClient (string serverName, string pipeName)
            {
                Client = new NamedPipeClientStream(".", pipeName, PipeDirection.InOut, PipeOptions.None,
                        TokenImpersonationLevel.None);
                Client.Connect();
                PipeReader = new StreamReader(Client);
                PipeWriter = new StreamWriter(Client) { AutoFlush = true };
            }
    
            public string DoRequest(string input)
            {
                if (Client.IsConnected == false)
                {
                    throw new Exception($"连接通过已断开");
                }
                Send(input);
                return Receive();
            }
    
            public void CloseServer()
            {
                Send(Exit);
            }
        }
    

    3、编写Pipe管道客户端PipeServer类(需引入 System.Security.Principal命名空间):

     public class PipeServer : PipeBase
        {
            private NamedPipeServerStream Server { get; set; }
            public Action<string> Received { get; set; }
            public PipeServer(string pipeName)
            {
                Server = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1);
                Server.WaitForConnection();
                PipeReader = new StreamReader(Server);
                PipeWriter = new StreamWriter(Server) { AutoFlush = true };
                BeginReceive();
            }
    
            public new void Send(string input)
            {
                if (Server.IsConnected == false)
                {
                    throw new Exception($"连接通过已断开");
                }
                base.Send(input);
            }
    
            private void BeginReceive()
            {
               new Thread(() =>
               {
                   try
                   {
                       while (true)
                       {
                           if (Server.IsConnected == false)
                           {
                               throw new Exception($"连接通过已断开");
                           }
                           string data = Receive();
                           Received?.Invoke(data);
                       }
                   }
                   catch (Exception e)
                   {
                       Console.WriteLine(e);
                   }
                 
               }).Start();
            }
        }
    

    4、编写测试用例

    (1)编写服务端测试用例,编写一个控制台程序

       class Program
        {
            private static PipeServer Server { get; set; }
            static void Main(string[] args)
            {
                try
                {
                    Server = new PipeServer("OEB");
                    Console.WriteLine("连接成功");
                    Server.Received += OnReceived;
                        Console.ReadKey();
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    Console.ReadKey();
                }
            }
    
            static void OnReceived(string message)
            {
                try
                {
                    Console.WriteLine($"{DateTime.Now:HH:mm:ss:fff} 收到客户端请求:{message}");
                    Server.Send($"Server Reply-{message}");
                }
                catch (Exception e)
                {
                    Console.WriteLine(e);
                    Environment.Exit(0);
                }
            }
        }
    

    (2)编写客户端程序,创建一个WPF程序

      public partial class MainWindow : Window
        {
            public static readonly DependencyProperty InputProperty = DependencyProperty.Register("Input", typeof(string), typeof(MainWindow), new PropertyMetadata("test data"));
            public static readonly DependencyProperty OutputProperty = DependencyProperty.Register("Output", typeof(string), typeof(MainWindow), new PropertyMetadata(""));
            public string Output
            {
                get { return (string)GetValue(OutputProperty); }
                set { SetValue(OutputProperty, value); }
            }
            public string Input
            {
                get { return (string)GetValue(InputProperty); }
                set { SetValue(InputProperty, value); }
            }
    
           private PipeClient Client { get; set; }
            public MainWindow()
            {
                InitializeComponent();
    
                Loaded += MainWindow_Loaded;
    
                Closed += MainWindow_Closed;
            }
    
            private void MainWindow_Closed(object sender, EventArgs e)
            {
               Client.CloseServer();
            }
    
            private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
            {
                await Task.Run(() =>
                {
                    Client = new PipeClient(".", "OEB");
                    this.Dispatcher?.Invoke(() =>
                    {
                        Input = "连接服务端成功...";
                    });
                });
            }
    
            private async void Button_Click(object sender, RoutedEventArgs e)
            {
                string input = Input;
                await Task.Run(() =>
                {
                    try
                    {
                        this.Dispatcher?.Invoke(() =>
                        {
                            Output += $"{DateTime.Now:HH:mm:ss:fff} 发送请求:{input}\r\n";
                        });
                        string output = Client.DoRequest(input); ;
                        this.Dispatcher?.Invoke(() =>
                        {
                            Output += $"{DateTime.Now:HH:mm:ss:fff} 收到服务端回复:{output}\r\n";
                        });
                    }
                    catch (Exception exception)
                    {
                        MessageBox.Show($"断开连接:{exception.InnerException}");
                    }
                });
            }
        }
    

    (3)测试效果

    image-20211230203335398

  • 相关阅读:
    算法题解:旋转数组的最小数字
    算法题解:连续子数组的最大和及其下标
    算法题解:快速排序算法(维基百科版)
    c++入门之类——进一步剖析
    c++入门之运算符重载
    c++入门之浅入浅出类——分享给很多想形象理解的人
    c++入门之再话内存和引用
    c++入门之引用
    c++入门之内置数组和array比较
    c++入门之结构体初步
  • 原文地址:https://www.cnblogs.com/dongweian/p/15750448.html
Copyright © 2011-2022 走看看