命名管道

305 阅读2分钟

介绍

管道是指连接一个读进程和一个写进程以实现他们之间通信的一个共享文件,又称为pipe文件,管道内数据以字符流的形式传输

管道机制

  • 互斥,一个进程在进行读/写操作,另一个线程必须等待
  • 同步,生产者消费者问题,写进程写完便挂起,知道读进程读取之后唤醒
  • 双方必须存在才能进行通信

缺点

  • 半双工通信,数据只能单向流动(双向交替通信又称为半双工通信,即通信的双方都可以发送信息,但不能双方同时发送(当然也就不能同时接收)。这种通信方式是一方发送另一方接收,过一段时间后再反过来。
  • 只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。于是出现了流管道(s_pipe)和有名管道(named pipe)

命名管道

克服了管道没有名字的限制,允许无亲缘关系进程间的通信,但还是半双工的

.NET 示例代码

public class NamePipeService
{
    /// <summary>
    /// 日志消息回调
    /// </summary>
    private Action<string> _logMsgCallbackAction;


    private ConcurrentDictionary<string, TaskFactory> pipeServerFactories =
        new ConcurrentDictionary<string, TaskFactory>();

    /// <summary>
    /// 初始化
    /// </summary>
    /// <param name="logMsgCallbackAction"></param>
    /// <returns></returns>
    public NamePipeService Init(Action<string> logMsgCallbackAction)
    {
        _logMsgCallbackAction = logMsgCallbackAction;

        return this;
    }

    /// <summary>
    /// 创建接收客户端
    /// </summary>
    /// <param name="receiveSuccessCallback">传入pipeName,msg</param>
    /// <param name="pipeNames"></param>
    /// <returns></returns>
    public NamePipeService BuildReceiveServiceClient(Action<string, string> receiveSuccessCallback,
        params string[] pipeNames)
    {
        if (pipeNames == null || !pipeNames.Any()) return this;

        foreach (var pipe in pipeNames)
        {
            this.CreateReceiveClient(pipe, msg => { receiveSuccessCallback.Invoke(pipe, msg); });
        }

        return this;
    }


    /// <summary>
    /// 发送消息(每条消息都得重新创建管道流)
    /// </summary>
    /// <param name="pipeName">管道名称</param>
    /// <param name="msg">消息内容</param>
    /// <param name="maxNumberOfServerInstances">命名管道的最大的创建个数(window:1~254,Unix>1)</param>
    public async Task SendMessage(string pipeName, string msg, int maxNumberOfServerInstances = 100)
    {
        if (!this.pipeServerFactories.ContainsKey(pipeName))
        {
            this.pipeServerFactories.TryAdd(pipeName, new TaskFactory(new SequentialScheduler()));
        }

        var currentTask = this.pipeServerFactories[pipeName];

       await currentTask.StartNew(async () =>
        {
            try
            {
                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
                {
                    PipeSecurity ps = new PipeSecurity();
                    PipeAccessRule psRule =
     new PipeAccessRule(@"Everyone", PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow);
                    ps.AddAccessRule(psRule);
                    await using var pipeServer =
     NamedPipeServerStreamAcl.Create(pipeName, PipeDirection.InOut, maxNumberOfServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, 1024, 1024, ps);
                    //等待连接
                    await pipeServer.WaitForConnectionAsync();
                    using var pipe = new PipeStreamEx(pipeServer);

                    pipe.Send(msg);

                    await pipeServer.FlushAsync();
                }
                else
                {
                    await using var pipeServer =
                        new NamedPipeServerStream("/tmp/"+pipeName, PipeDirection.InOut, maxNumberOfServerInstances,PipeTransmissionMode.Byte);
                    //等待连接
                    await pipeServer.WaitForConnectionAsync();
                    using var pipe = new PipeStreamEx(pipeServer);

                    pipe.Send(msg);

                    await pipeServer.FlushAsync();
                }
            }
            catch (IOException e)
            {
                _logMsgCallbackAction.Invoke($"{pipeName}NamePipeService error: {e.Message}");
            }
            catch (Exception e)
            {
                _logMsgCallbackAction.Invoke($"{pipeName}NamePipeService error: {e.Message}");
            }
        });
    }

    /// <summary>
    /// 创建pipe接收消息的监听客户端
    /// </summary>
    /// <param name="pipeName"></param>
    /// <param name="receiveSuccessCallback"></param>
    public void CreateReceiveClient(string pipeName, Action<string> receiveSuccessCallback)
    {
        //创建一个线程,监听接收消息
         Task.Factory.StartNew(() =>
        {
            while (true)
            {
                try
                {
                    using var pipeClient =
                        new NamedPipeClientStream(@".", pipeName, PipeDirection.InOut);

                    pipeClient.Connect();

                    using var pipe = new PipeStreamEx(pipeClient);

                    var msg = pipe.Receive();

                    receiveSuccessCallback?.Invoke(msg);
                }
                catch (Exception e)
                {
                    Console.WriteLine($"{pipeName}-CreateReceive error,message:{e.Message}");
                }
            }
        });
    }
}