I wrote a small client and server app to learn how to use pipes. In each app I originally had the streams in a using block and I quickly found out that when the using block was finished and it disposed of the stream, it also disposed my pipes.
I got rid of the using blocks and made member variables for the streams. Now my problem is that when I call the ReadLine function on the StreamReader in the client it doesn’t continue until my server app is closed (or more specifically, until the StreamWriter is disposed of).
It seems strange that I would have to create a new stream (and by extension a new pipe as well since every time the stream is closed it disposes of the pipe too) for each message I want to send. What do I need to change?
Server Code:
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);
}
}
Client Code:
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);
}
}
Server form contains one text box and has the following code:
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();
}
}
Client form has one text box and has the following code:
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);
}
}
As you can see I’m trying to fire an event every time a message is sent, and I want the pipe to stay open and listen continually for messages.
In order to give a proper answer to your question I would have to have a SSCCE that I can run and fiddle with. Unfortunately, the code you posted isn’t one. So, the next best thing I can do is give you a pointer to the direction in which you should look to solve your problem: the NamedPipeServerStream() function has many overloads, some of which allow many more parameters to be specified. You should pick one of those overloads and try specifying more parameters, fiddling with their values, in order to get your code to work. One of them, rather crucial, I would say, is the pipe transmission mode: byte or message. Try specifying ‘message’ for that one.
EDIT
Okay, I think I see two problems with the code posted:
WriteLine() most probably uses buffered I/O, which means that you would have to issue lots of WriteLine()s before the buffer would fill up and be sent to the client. When you terminate the server, its buffer is flushed, that’s why the messages used to arrive to the client at that moment.
The tight blocking ReadLine() loop on the client probably does not allow the client to do any repainting, so setting the text of the textbox to the content of the message received probably does not appear to have any effect. (Unless setting the text property forces a repaint, I am not sure it does, it should not.) That could be solved by either following
textBox1.Text = o;withtextBox1.Update();or by embedding aSystem.WinForms.Application.DoEvents();call inside your tight loop.Please note that you have the Server form and the Client form mixed up; that’s one of the reasons why a real SSCCE (a listing of code known to compile) is useful.
I would say, welcome to the wonderful world of windows named pipes programming. It is not simple, and it is not easy. MSDN has a good sample here: How to: Use Named Pipes to Communicate Between Processes over a Network but as extensive as it may seem, it is still quite elementary. In reality you will need to either use asynchronous (non-blocking) I/O or utilize the thread pool (System.Threading.ThreadPool) if you want your server to be able to handle an arbitrary number of simultaneously connected clients.