如何“重用”流?

时间:2012-02-26 21:51:57

标签: c# .net stream pipe

我写了一个小客户端和服务器应用程序来学习如何使用管道。在每个应用程序中,我最初在一个使用块中有流,我很快发现当使用块完成并处理了流时,它也处理了我的管道。

我摆脱了使用块并为流创建了成员变量。现在我的问题是,当我在客户端的StreamReader上调用ReadLine函数时,它不会继续,直到我的服务器应用程序关闭(或者更具体地说,直到StreamWriter被丢弃)。

我想要发送的每条消息都需要创建一个新的流(并且通过扩展一个新的管道,因为每次流关闭它也会处理管道)。我需要改变什么?

服务器代码:

class PipeServer
{
    NamedPipeServerStream _pipeServer;
    StreamWriter _sw;

    public PipeServer(string pipeName)
    {
        _pipeServer = new NamedPipeServerStream(pipeName, PipeDirection.Out);
        _pipeServer.WaitForConnection();
        _sw = new StreamWriter(_pipeServer) { AutoFlush = true };
    }

    public void WriteMessage(string message)
    {
        _sw.WriteLine(message);
    }
}

客户代码:

public delegate void MessageReadEventHandler(string message);

class PipeClient
{
    public event MessageReadEventHandler MessageReadEvent;

    NamedPipeClientStream _pipeClient;
    StreamReader _sr;

    public PipeClient(string pipeName)
    {
        _pipeClient = new NamedPipeClientStream(".", pipeName, PipeDirection.In);
        _pipeClient.Connect();
        _sr = new StreamReader(_pipeClient);
    }

    public void ReadMessages()
    {
        string temp;

        while ((temp = _sr.ReadLine()) != null)
            if (MessageReadEvent != null)
                MessageReadEvent(temp);
    }
}

服务器表单包含一个文本框,其代码如下:

public partial class Form1 : Form
{
    PipeClient pClient = new PipeClient("testpipe");

    public Form1() { InitializeComponent(); }

    private void Form1_Load(object sender, EventArgs e)
    {
        pClient.MessageReadEvent += o => { textBox1.Text = o; };
    }

    private void Form1_Shown(object sender, EventArgs e)
    {
        pClient.ReadMessages();
    }
}

客户表单有一个文本框,其代码如下:

public partial class Form1 : Form
{
    PipeServer pServer = new PipeServer("testpipe");

    public Form1() { InitializeComponent(); }

    private void ServerTextBox_TextChanged(object sender, EventArgs e)
    {
        pServer.WriteMessage(ServerTextBox.Text);
    }
}

正如您所看到的,我每次发送消息时都会尝试触发事件,并且我希望管道保持打开状态并不断收听消息。

1 个答案:

答案 0 :(得分:1)

为了对你的问题给出正确的答案,我必须有一个SSCCE,我可以运行和摆弄。不幸的是,您发布的代码不是一个。所以,我能做的下一个最好的事情是给你一个指向你应该解决问题的方向的指针:NamedPipeServerStream()函数有很多重载,其中一些重载允许指定更多的参数。您应该选择其中一个重载并尝试指定更多参数,摆弄它们的值,以使代码工作。其中一个,相当重要的,我会说,是管道传输模式:字节或消息。尝试为那个指定“消息”。

修改

好的,我认为发布的代码存在两个问题:

  1. WriteLine()最有可能使用缓冲I / O,这意味着您必须在缓冲区填满并发送到客户端之前发出大量WriteLine()。当您终止服务器时,它的缓冲区被刷新,这就是当时用于到达客户端的消息的原因。

  2. 客户端上的严格阻塞ReadLine()循环可能不允许客户端进行任何重新绘制,因此将文本框的文本设置为收到的消息内容可能似乎没有任何效果。 (除非设置文本属性强制重新绘制,否则我不确定它是否存在,它不应该。)可以通过textBox1.Text = o;textBox1.Update();或嵌入System.WinForms.Application.DoEvents();调用来解决在紧密的循环中。

  3. 请注意,您将服务器表单与客户表单混为一谈;这就是为什么真正的SSCCE(已知编译代码列表)有用的原因之一。

    我想说,欢迎来到名为管道编程的精彩世界。这并不简单,也不容易。 MSDN在这里有一个很好的样本:How to: Use Named Pipes to Communicate Between Processes over a Network但是看起来很广泛,它仍然是非常基础的。实际上,如果您希望服务器能够处理任意数量的同时连接的客户端,则需要使用异步(非阻塞)I / O或利用线程池(System.Threading.ThreadPool)。