【问题标题】:How To Asynchronously Read/Write A Named Pipe In C#如何在 C# 中异步读取/写入命名管道
【发布时间】:2011-05-28 05:42:27
【问题描述】:

我有一个托管自定义用户控件的 Windows 窗体。此用户控件启动一个单独的进程 (.exe),该进程创建并初始化 NamedPipeServerStream。一旦进程初始化了 NamedPipeServerStream,用户控件将作为 NamedPipeClientStream 连接到它。

这一切都很好。

在 Windows 窗体上,我有一个名为“检查更新”的按钮。当按下此按钮时,NamedPipeClientStream 将向服务器发送一条消息,服务器对此作出响应,消息框显示“我已被告知检查更新”。所以我可以告诉这个客户端 > 服务器通信工作正常。

这就是问题所在。然后服务器应该向客户端发送一条 BACK 消息,告诉它它现在正在检查更新(因此用户控件可以在服务器接收到它的命令后更新它的状态)。但每当这种情况发生时,一切都会锁定。

现在我假设这是因为我试图同时读取和写入命名管道?

以下是来自服务器和客户端的一些代码 sn-ps。 (这两个 sn-ps 在各自进程中的单独线程中运行,以免阻塞 UI)

GUpdater(命名管道服务器):

private void WaitForClientCommands()
{
    // Wait for a connection from a Named Pipe Client (The GUpControl)
    pipeStream.WaitForConnection();
    pipeConnected = true;

    // Create a StreamWriter/StreamReader so we can write/read to the Named Pipe
    pipeStreamWriter = new StreamWriter(pipeStream) { AutoFlush = true };
    pipeStreamReader = new StreamReader(pipeStream);

    // Now that we have a connection, start reading in messages and processing them
    while (true)
    {
        // Skip this time if we are currently writing to the pipe
        if(isWritingToPipe) continue;

        var message = pipeStreamReader.ReadLine();
        if (message == null)
        {
            // We don't want to hog up all the CPU time, so if no message was reaceived this time, wait for half a second
            Thread.Sleep(500);
            continue;
        }

        switch(message)
        {
            case "CheckForUpdates":
                //MessageBox.Show("Told to check for updates");
                SendMessageToClient("Checking For Updates, Woot!");
                break;
            case "DownloadUpdate":
                MessageBox.Show("Told to download update");
                break;
            case "ApplyUpdate":
                MessageBox.Show("Told to apply update");
                break;
        }
    }
}

GUpControl(命名管道客户端):

private void WaitForServerCommands()
{
    if(!pipeConnected) return;

    // Now that we have a connection, start reading in messages and processing them
    while (true)
    {
        // Skip this time if we are currently writing to the pipe
        if (isWritingToPipe) continue;

        // Attempt to read a line from the pipe
        var message = pipeStreamReader.ReadLine();
        if (message == null)
        {
            // We don't want to hog up all the CPU time, so if no message was reaceived this time, wait for half a second
            Thread.Sleep(500);
            continue;
        }

        MessageBox.Show("I got a message from the server!!\r\n" + message);
    }
}

下面的sn-p是负责从各个组件写入Client/Server的方法。 (唯一的区别在于名称,即SendMessageToClientSendMessageToServer

private void SendMessageToServer(string message)
{
    if(pipeConnected)
    {
        isWritingToPipe = true;
        pipeStreamWriter.WriteLine(message);
        isWritingToPipe = false;
    }
}

isWritingToPipe 变量是一个简单的布尔值,当相应的进程尝试写入命名管道时为真。这是我解决问题的初步尝试。

非常感谢任何帮助!

【问题讨论】:

  • 任何完整的源代码示例?

标签: c# named-pipes


【解决方案1】:

System.IO.Pipes.PipeStream 有一个方法WaitForPipeDrain(),这是在交换消息时管理客户端和服务器之间协调的正确方法。

管道的传输模式也可能会影响一些事情:您使用的是字节模式还是消息模式?例如,我不确定将流包装在 StreamReader 中与管道消息模式语义的效果如何。有关消息模式的更多信息,请参阅this SO answer

【讨论】:

    【解决方案2】:

    这个问题的解决方案原来是重新考虑软件的设计。

    UserControl 不是在更新过程的每个步骤都与单独的应用程序对话,而是由 UserControl 自己处理,然后最终在需要应用更新时(这涉及覆盖应用程序文件,因此需要单独的进程)它只是使用 Process.Start("...") 以及一些命令行参数来调用单独的进程来完成工作。

    比尝试为此任务进行进程间通信更简单的解决方案。

    不过,如果能找出我的双向通信不起作用的原因,那就太好了。

    【讨论】:

    • Tom,很高兴您找到了更好的解决方案,但为了与网站的目的保持一致,不应将其标记为答案,因为它没有回答标题中提出的问题。
    【解决方案3】:

    当您向服务器发送信号以检查更新时,请在 BackgroundWorker 类中执行此操作

    【讨论】:

    • 您好 Saurabh,感谢您的建议。不幸的是,当我尝试写入 pipeStreamWriter 时,这会导致“管道损坏”错误。
    • @Tom Glenn 你检查过 PipeDirection 枚举吗? msdn.microsoft.com/en-us/library/bb344934.aspx
    • 是的,不幸的是它没有任何区别。 :(
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多