通过NamedPipe命名管道实现进程间通信(C#实现,包括客户端和服务端)

74 篇文章 29 订阅

一、前言

最近在做一个这样的功能,Unity发布PC平台的客户端exe,在客户端exe中拉起浏览器exe(这个浏览器是自己使用winform实现的),浏览器内调用JsApi执行支付,支付结果会回调游戏服务端URL,服务端通过消息通知客户端exe,此时客户端exe通过进程间通信让浏览器exe关闭。

好了,进程间怎么通信呢?
实现进程间通信有很多中方案:管道(pipe)、命名管道(FIFO)、消息队列(MessageQueue)、共享存储(SharedMemory)、信号量(Semaphore)、套接字(Socket)、信号 ( sinal)等。

本文要实现的是命名管道的方案。
在这里插入图片描述

二、关于命名管道

命名管道又名命名管线Named Pipes),是一种简单的进程间通信(IPC)机制,Microsoft Windows大都提供了对它的支持(但不包括Windows CE)。命名管道可在同一台计算机的不同进程之间或在跨越一个网络的不同计算机的不同进程之间,支持可靠的、单向或双向的数据通信。
命名管道的所有实例拥有相同的名称,但是每个实例都有其自己的缓冲区和句柄。
windows中命名管道的通信方式是:1、创建命名管道;2、连接命名管道;3读写命名管道。

三、运行效果

在这里插入图片描述

四、Demo工程源码

Demo工程源码我已上传到CODE CHINA,感兴趣的同学可以下载下来学习。
地址:https://codechina.csdn.net/linxinfa/csharp-namedpipe-demo
在这里插入图片描述

五、核心代码

具体代码详见上面的Demo工程。

服务端

/// <summary>
/// 服务端管道
/// </summary>
class NamedPipeServer
{
    private NamedPipeServerStream Server { get; set; }
    private byte[] Buffer;
    private int BufferSize = 10;
    private StringBuilder InputStr { get; set; }

    public NamedPipeServer(string name)
    {
        this.Server = new NamedPipeServerStream(name,
            PipeDirection.InOut,
            1,
            PipeTransmissionMode.Message,
            PipeOptions.None);
        this.InputStr = new StringBuilder();
        Buffer = new byte[BufferSize];
    }

    public void Start()
    {
        while (true)
        {
            this.Server.WaitForConnection();

            AsyncState asyncState = new AsyncState()
            {
                Buffer = new byte[BufferSize],
                EvtHandle = new ManualResetEvent(false),
                Stream = this.Server
            };

            //异步读取,并阻塞线程,读取结束取消阻塞
            this.Server.BeginRead(this.Buffer, 0, this.Buffer.Length, new AsyncCallback(ReadCallback), asyncState);
            asyncState.EvtHandle.WaitOne();

            //获取输出字符串
            string outStr = "";
            if (Readed != null) outStr = this.Readed.Invoke(this.InputStr.ToString().Replace("\0", " ").Trim()).Trim();
            this.InputStr.Clear();
            for (int i = 0; i < this.BufferSize; i++)
                outStr = " " + outStr;

            //输出到内存流,然后内存流转写字节码到服务流中
            using (MemoryStream memoryStream = new MemoryStream())
            using (StreamWriter write = new StreamWriter(memoryStream))
            {
                write.Write(outStr);
                write.Flush();
                memoryStream.Flush();
                int length = 0;
                memoryStream.Position = 0;
                byte[] tmp = new byte[BufferSize];
                while (((length = memoryStream.Read(tmp, 0, this.Buffer.Length)) != 0))
                {
                    Server.Write(tmp, 0, length);
                }
            }

            Server.WaitForPipeDrain();
            Server.Flush();
            Server.Disconnect();
        }
    }


    private void ReadCallback(IAsyncResult arg)
    {
        AsyncState state = arg.AsyncState as AsyncState;
        int length = state.Stream.EndRead(arg);

        if (length > 0)
        {
            byte[] buffer;
            if (length == BufferSize) buffer = state.Buffer;
            else
            {
                buffer = new byte[length];
                Array.Copy(state.Buffer, 0, buffer, 0, length);
            }

            if (state.MemoryStream == null) state.MemoryStream = new MemoryStream();
            state.MemoryStream.Write(buffer, 0, buffer.Length);
            state.MemoryStream.Flush();
        }
        if (length < BufferSize)
        {
            state.MemoryStream.Position = 0;
            using (StreamReader reader = new StreamReader(state.MemoryStream))
            {
                this.InputStr.Append(reader.ReadToEnd());
            }
            state.MemoryStream.Close();
            state.MemoryStream.Dispose();
            state.MemoryStream = null;

            state.EvtHandle.Set();
        }
        else
        {
            Array.Clear(state.Buffer, 0, BufferSize);
            //再次执行异步读取操作
            state.Stream.BeginRead(state.Buffer, 0, BufferSize, new AsyncCallback(ReadCallback), state);
        }
    }
    public event Func<string, string> Readed;
}

客户端

/// <summary>
/// 客户端管道
/// </summary>
class NamedPipeClient
{
    private NamedPipeClientStream Client { get; set; }
    private byte[] Buffer { get; set; }
    private bool IsReadEnd { get; set; }
    private bool IsWriteEnd { get; set; }
    private int BufferSize = 10;
    private StringBuilder InputStr { get; set; }
    private string OutputStr { get; set; }

    public NamedPipeClient(string serverName, string serverHost)
    {
        Client = new NamedPipeClientStream(serverHost, serverName);
        this.Buffer = new byte[BufferSize];
        this.InputStr = new StringBuilder();
    }

    public string Request(string outPutStr)
    {
        this.OutputStr = outPutStr.Trim();
        this.Client.Connect();

        for (int i = 0; i < this.BufferSize; i++)
            this.OutputStr = " " + this.OutputStr;
        using (MemoryStream memoryStream = new MemoryStream())
        using (StreamWriter writer = new StreamWriter(memoryStream))
        {

            writer.Write(OutputStr);
            writer.Flush();
            memoryStream.Flush();
            int length = 0;
            memoryStream.Position = 0;
            while ((length = memoryStream.Read(Buffer, 0, Buffer.Length)) != 0)
            {
                this.Client.Write(Buffer, 0, length);
            }
            this.Client.WaitForPipeDrain();
            this.Client.Flush();
        }

        AsyncState asyncState = new AsyncState()
        {
            Buffer = new byte[BufferSize],
            EvtHandle = new ManualResetEvent(false),
            Stream = this.Client
        };

        IAsyncResult readAsyncResult = this.Client.BeginRead(this.Buffer, 0, this.Buffer.Length, new AsyncCallback(ReadCallback), asyncState);
        asyncState.EvtHandle.WaitOne();

        this.Client.Close();
        this.Client.Dispose();

        return this.InputStr.ToString().Replace("\0", " ").Trim();
    }

    private void ReadCallback(IAsyncResult arg)
    {
        AsyncState state = arg.AsyncState as AsyncState;
        int length = state.Stream.EndRead(arg);

        if (length > 0)
        {
            byte[] buffer;
            if (length == BufferSize) buffer = state.Buffer;
            else
            {
                buffer = new byte[length];
                Array.Copy(state.Buffer, 0, buffer, 0, length);
            }

            if (state.MemoryStream == null) state.MemoryStream = new MemoryStream();
            state.MemoryStream.Write(buffer, 0, buffer.Length);
            state.MemoryStream.Flush();
        }
        if (length < BufferSize)
        {
            state.MemoryStream.Position = 0;
            using (StreamReader reader = new StreamReader(state.MemoryStream))
            {
                this.InputStr.Append(reader.ReadToEnd());
            }
            state.MemoryStream.Close();
            state.MemoryStream.Dispose();
            state.MemoryStream = null;

            state.EvtHandle.Set();
        }
        else
        {
            Array.Clear(state.Buffer, 0, BufferSize);
            //再次执行异步读取操作
            state.Stream.BeginRead(state.Buffer, 0, BufferSize, new AsyncCallback(ReadCallback), state);
        }
    }
}
### 回答1: Dart 中可以使用命名管道实现进程间通信。以下是在 Windows 下使用命名管道进行进程间通信的示例代码: ```dart import 'dart:io'; // 创建服务端 void server() { var server = NamedPipeServer('\\\\.\\pipe\\my_pipe'); server.listen((pipe) { pipe.write('Hello, client!'); pipe.flush(); pipe.close(); server.close(); }); } // 创建客户端 void client() { var client = NamedPipeClient('\\\\.\\pipe\\my_pipe'); client.connect().then((pipe) { pipe.listen((data) { print('Received: $data'); pipe.close(); }); }); } void main() { var isServer = bool.fromEnvironment("server", defaultValue: false); if (isServer) { server(); } else { client(); } } ``` 上面的示例中,服务端使用了 NamedPipeServer 来创建命名管道,并监听客户端的连接。客户端使用 NamedPipeClient 来连接命名管道,并监听服务端的数据。 可以通过在终端运行 dart file.dart server 启动服务端,或者运行 dart file.dart 启动客户端来运行上面的示例。 ### 回答2: Dart 是一种使用面向对象编程思想的编程语言,具有灵活性和高效性,可用于开发跨平台的应用程序。在 Dart 中,可以使用命名管道实现进程间的通信。下面是一个使用 Dart 实现进程间通信的 Windows 示例。 在 Windows 系统中,可以使用 Win32 API 来创建和管理命名管道。首先,需要导入 dart:ffi 包并定义需要使用的 Win32 API 函数,如 CreateNamedPipe、ConnectNamedPipe、WriteFile 和 ReadFile 等。接下来,可以通过调用这些函数来创建命名管道,并向其中写入和读取数据。 首先,我们可以使用 CreateNamedPipe 函数来创建一个命名管道。例如: ``` final path = r'\\.\pipe\my_named_pipe'; // 定义管道的名称 final pipe = CreateNamedPipe( path, PipeOpenMode.PIPE_ACCESS_DUPLEX, PipeMode.PIPE_TYPE_MESSAGE | PipeMode.PIPE_READMODE_MESSAGE | PipeMode.PIPE_WAIT, PipeUnlimitedInstances, PipeBufferInSize, PipeBufferOutSize, PipeDefaultTimeOut, nullptr, // 默认安全属性 ); if (pipe == INVALID_HANDLE_VALUE) { // 命名管道创建失败 return; } ``` 接下来,使用 ConnectNamedPipe 函数来监听管道连接请求。示例如下: ``` final isSuccess = ConnectNamedPipe(pipe, nullptr); if (!isSuccess && GetLastError() != ERROR_PIPE_CONNECTED) { // 命名管道连接失败 CloseHandle(pipe); return; } ``` 然后,可以使用 WriteFile 函数来向管道中写入数据。示例如下: ``` final message = 'Hello, Pipe!'; final bytes = Utf8Encoder().convert(message); final bytesWritten = Uint32List(1); WriteFile( pipe, bytes.buffer.asPointer(), bytes.lengthInBytes, bytesWritten, nullptr, ); ``` 最后,使用 ReadFile 函数来从管道中读取数据。示例如下: ``` final buffer = Uint8List(PipeBufferInSize); final bytesRead = Uint32List(1); ReadFile( pipe, buffer.buffer.asPointer(), PipeBufferInSize, bytesRead, nullptr, ); ``` 以上就是使用 Dart 在 Windows 系统中实现进程间通信命名管道示例。通过使用命名管道,我们可以方便地在不同的进程之间传递数据,实现进程间的通信。 ### 回答3: Dart是一种流行的编程语言,可以用于开发跨平台的应用程序。在Windows操作系统中,进程间通信是一个重要的机制,用于不同进程之间的数据传输和共享。 Dart提供了一个命名管道(Named Pipe)的概念,作为进程间通信的一种方式。命名管道允许不同进程之间通过共享的管道进行双向通信。以下是一个使用Dart的命名管道进行进程间通信的示例: 1. 首先,我们需要创建一个命名管道。在Dart中,可以使用`File`类来创建和操作文件,因此我们可以使用`File`类的`create`方法来创建命名管道,例如: ```dart import 'dart:io'; void createNamedPipe() { var pipe = File(r'\\.\pipe\myNamedPipe'); pipe.createSync(); } ``` 2. 接下来,我们可以创建一个进程,用于读取和写入命名管道的数据。在Dart中,可以使用`Process`类来创建和操作进程,可以使用`Process.start`方法创建一个新进程,并指定要执行的命令和参数,例如: ```dart import 'dart:io'; void createProcess() async { var process = await Process.start('myProcess.exe', []); // 通过进程的标准输入,写入数据到命名管道 process.stdin.writeln('Hello from Dart!'); // 通过进程的标准输出,读取命名管道中的数据 print(await process.stdout.transform(utf8.decoder).join()); } ``` 3. 在命名管道的另一端,我们可以创建另一个Dart进程来读取和写入管道中的数据,方法类似于上述步骤。 总之,使用Dart的命名管道进行进程间通信,在Windows操作系统中是一种可行的方法。通过创建命名管道和使用`Process`类,我们可以实现进程之间的数据传输和共享。这为我们开发更复杂的应用程序提供了便利,例如多进程的服务器或基于管道的并行处理程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林新发

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值