我编写了一个小型客户端和服务器应用程序来学习如何使用管道。在每个应用程序中,我最初都有一个使用块中的流,我很快发现,当使用块完成并处理了流时,它也处理了我的管道。
我去掉了使用块,并为流创建了成员变量。现在我的问题是,当我在客户端的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"。
编辑
好吧,我想我看到发布的代码有两个问题:
-
WriteLine()很可能使用缓冲I/O,这意味着在缓冲区填满并发送到客户端之前,您必须发出大量的WriteLines()。当您终止服务器时,它的缓冲区会被刷新,这就是为什么消息会在那一刻到达客户端。
-
客户端上的紧密阻塞ReadLine()循环可能不允许客户端进行任何重新绘制,因此将文本框的文本设置为接收到的消息的内容似乎没有任何效果。(除非设置text属性强制重新绘制,否则我不确定它是否会,它不应该。)这可以通过用
textBox1.Update();
跟随textBox1.Text = o;
来解决,也可以通过在紧循环中嵌入System.WinForms.Application.DoEvents();
调用来解决。
请注意,您将服务器表单和客户端表单混合在一起;这就是为什么真正的SSCCE(一个已知可编译代码的列表)是有用的原因之一。
我想说,欢迎来到windows命名管道编程的奇妙世界。这并不简单,也不容易。MSDN在这里有一个很好的示例:如何:使用命名管道在网络上的进程之间进行通信,尽管它看起来很广泛,但它仍然很初级。事实上,如果您希望服务器能够处理任意数量的同时连接的客户端,则需要使用异步(非阻塞)I/O或使用线程池(System.Threading.ThreadPool)。