如何"reuse"流?



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

我去掉了使用块,并为流创建了成员变量。现在我的问题是,当我在客户端的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);
    }
}

正如你所看到的,我试图在每次发送消息时触发一个事件,我希望管道保持打开状态,并不断地侦听消息。

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

编辑

好吧,我想我看到发布的代码有两个问题:

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

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

请注意,您将服务器表单和客户端表单混合在一起;这就是为什么真正的SSCCE(一个已知可编译代码的列表)是有用的原因之一。

我想说,欢迎来到windows命名管道编程的奇妙世界。这并不简单,也不容易。MSDN在这里有一个很好的示例:如何:使用命名管道在网络上的进程之间进行通信,尽管它看起来很广泛,但它仍然很初级。事实上,如果您希望服务器能够处理任意数量的同时连接的客户端,则需要使用异步(非阻塞)I/O或使用线程池(System.Threading.ThreadPool)。

相关内容

  • 没有找到相关文章

最新更新