C# NamePipe使用小结

最近在一次项目中使用到了C#中命名管道,所以在此写下一篇小结备忘。

为什么要使用命名管道呢?为了实现两个程序之间的数据交换。假设下面一个场景。在同一台PC上,程序A与程序B需要进行数据通信,此时我们就可以使用命名管道技术来实现。命名管道的两个对象。NamedPipeClientStream NamedPipeServerStream 对象。请求通信的一方为Client端,发送数据的一方为Server端。

使用NamedPipe来通信,如果Server端崩溃了,不会影响到客户端。

下面我们通过一个例子来说明:

Server端:

UI:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>
        
        <TextBlock Text="Received Message:" Grid.Row="1" Margin="10"/>
        <TextBlock x:Name="tblRecMsg" VerticalAlignment="Center" Grid.Row="1" Grid.Column="1"/>
        
        <Button Content="Send" Grid.Row="2" Margin="10" Click="OnSend"/>
        <TextBox x:Name="txtSendMsg" VerticalAlignment="Center" Grid.Row="2" Grid.Column="1" Margin="10"/>
    </Grid>

Code:

private NamedPipeServerStream _pipe;

        private const string PipeName = "PipeSample";

        private const int PipeInBufferSize = 4096;

        private const int PipeOutBufferSize = 65535;

        private Encoding encoding = Encoding.UTF8;

        public MainWindow()
        {
            InitializeComponent();

            _pipe = new NamedPipeServerStream
            (
                PipeName,
                PipeDirection.InOut,
                1,
                PipeTransmissionMode.Message,
                PipeOptions.Asynchronous | PipeOptions.WriteThrough,
                PipeInBufferSize,
                PipeOutBufferSize
             );

            _pipe.BeginWaitForConnection(WaitForConnectionCallback, _pipe);
        }

        private void WaitForConnectionCallback(IAsyncResult ar)
        {
            var pipeServer = (NamedPipeServerStream)ar.AsyncState;

            pipeServer.EndWaitForConnection(ar);

            var data = new byte[PipeInBufferSize];

            var count = pipeServer.Read(data, 0, PipeInBufferSize);

            if (count > 0)
            {
                // 通信双方可以约定好传输内容的形式,例子中我们传输简单文本信息。

                string message = encoding.GetString(data, 0, count);

                Dispatcher.BeginInvoke(new Action(() => 
                {
                    tblRecMsg.Text = message;
                }));
            }
        }

        private void OnSend(object sender, RoutedEventArgs e)
        {
            if (_pipe.IsConnected)
            {
                try 
                {
                    string message = txtSendMsg.Text;

                    byte[] data = encoding.GetBytes(message);

                    _pipe.Write(data, 0, data.Length);
                    _pipe.Flush();
                    _pipe.WaitForPipeDrain();
                }
                catch { }
            }

            Close();
        }

Client端:

UI:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto"/>
            <ColumnDefinition Width="*"/>
        </Grid.ColumnDefinitions>

        <Button Content="Connect" Margin="10" Click="OnConnect"/>
        
        <TextBlock Text="Received Message:" Grid.Row="1" Margin="10"/>
        <TextBlock x:Name="tblRecMsg" Grid.Row="1" Grid.Column="1"/>
    </Grid>

Code:

private const string PipeServerName = "PipeServer.exe";

        private const string PipeName = "PipeSample";

        private Encoding encoding = Encoding.UTF8;

        private NamedPipeClientStream _pipe;

        private bool _starting = false;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void OnConnect(object sender, RoutedEventArgs e)
        {
            if (_starting)
            {
                return;
            }

            var path = System.IO.Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, PipeServerName);

            var startInfo = new ProcessStartInfo(path)
            {
                UseShellExecute = false,
                CreateNoWindow = true
            };

            try
            {
                var process = Process.Start(startInfo);

                _pipe = new NamedPipeClientStream
                (
                    ".", 
                    PipeName, 
                    PipeDirection.InOut, 
                    PipeOptions.Asynchronous | PipeOptions.WriteThrough
                );

                _pipe.Connect();

                _pipe.ReadMode = PipeTransmissionMode.Message;

                string message = "Connected!";

                byte[] data = encoding.GetBytes(message);

                _pipe.BeginWrite(data, 0, data.Length, PipeWriteCallback, _pipe);

                _starting = true;
            }
            catch (Exception ex)
            {
                Debug.Write(ex.StackTrace);
            }
        }

        private void PipeWriteCallback(IAsyncResult ar)
        {
            var pipe = (NamedPipeClientStream)ar.AsyncState;

            pipe.EndWrite(ar);
            pipe.Flush();
            pipe.WaitForPipeDrain();

            var data = new byte[65535];

            var count = pipe.Read(data, 0, data.Length);

            if (count > 0)
            {
                string message = encoding.GetString(data, 0, count);

                Dispatcher.BeginInvoke(new Action(() => {
                    tblRecMsg.Text = message;
                }));
            }
        }

需要注意的地方:因为我们在同一台PC上面进行通信,我们只需要将 NamedPipeClientStream 构造参数中pipeServer设为"."即可。另外因为这只是一个示例,所以PipeServer中只传递简单String类型。当然也可以传递其他类型的内容。

运行效果:

感谢您的阅读!

原文地址:https://www.cnblogs.com/yang-fei/p/4696689.html