我想出了以下代码:
class IPCServer
{
private Thread ipcServerThread;
private NamedPipeServerStream pipeServer;
public IPCServer()
{
pipeServer = new NamedPipeServerStream("iMedCallInfoPipe", PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
}
public void SendMessage (String message) {
ThreadStart ipcServerThreadInfo = () => WriteToPipe(message);
ipcServerThread = new Thread(ipcServerThreadInfo);
ipcServerThread.Start();
}
private void WriteToPipe(String message)
{
if (pipeServer.IsConnected)
{
byte[] bytes = Encoding.UTF8.GetBytes(message);
pipeServer.Write(bytes, 0, bytes.Length);
pipeServer.WaitForPipeDrain();
pipeServer.Flush();
}
}
}
class ICPClient
{
public void Read(int TimeOut = 1000)
{
try
{
NamedPipeClientStream pipeStream = new NamedPipeClientStream(".", "iMedCallInfoPipe", PipeDirection.In, PipeOptions.None);
pipeStream.Connect(TimeOut);
using (StreamReader sr = new StreamReader(pipeStream))
{
string _buffer;
while ((_buffer = sr.ReadLine()) != null)
{
Console.WriteLine("Received from server: {0}", _buffer);
}
}
}
catch (TimeoutException)
{
}
}
}
这是管道通信解决方案的客户端服务器。但是我需要服务器以异步方式写入消息,并且客户端在弹出时也会异步地读取它们。我怎样才能做到这一点?有大量的样本,但大多数都考虑客户写入服务器,我不知道如何实现我的目标,特别是我已编写的代码...
答案 0 :(得分:2)
首先,你不需要两个管道。你可以在一个命名管道的两端写入/读取,它不会得到消息或排序,混淆。它也不会被卡住。只要你遵守基本的管道规则,双方都可以随心所欲地写作,并随时阅读。
其次,您应该使用消息模式,而不是字节模式,或者它可能将发送或读取的消息一起集中到一个缓冲区中。
第三,我非常确定如果你使用基于消息的管道,而不是字节,你不需要调用WaitForPipeDrain或Flush。我想这会让你不必要地放慢速度。如果我错了,有人会在下面纠正我吗?
最后,您可以随时随地阅读"在客户端上是:始终具有异常读取优秀。也就是说,一旦客户端连接到服务器,立即执行对给定缓冲区的异步读取,您知道该缓冲区足够大"抓住你可能获得的任何东西。当您调用异步读取回调(您提供给异步读取方法的那个)时,您将被告知填充了多少缓冲区。然后,您可以向客户端类的用户(您定义的事件)发出一个事件,或者您可以调用回调,或者您可以执行某些操作。完成事件回调后,立即执行另一个异步读取。如果没有什么可读的,那没关系,它会在那里等待一些东西进来。请注意,异步读取总是挂起,当远程管道关闭时,异步读取将返回关闭"管道关闭"和" 0字节读取"。这很正常。
这是我的读取方法的片段,在我的客户端类中:
public void StartReadingAsync()
{
// Debug.WriteLine("Pipe " + FullPipeNameDebug() + " calling ReadAsync");
// okay we're connected, now immediately listen for incoming buffers
//
byte[] pBuffer = new byte[MaxLen];
m_pPipeStream.ReadAsync(pBuffer, 0, MaxLen).ContinueWith(t =>
{
// Debug.WriteLine("Pipe " + FullPipeNameDebug() + " finished a read request");
// before we call the user back, start reading ANOTHER buffer, so the network stack
// will have something to deliver into and we don't keep it waiting.
// We're called on the "anonymous task" thread. if we queue another call to
// the pipe's read, that request goes down into the kernel, onto a different thread
// and this will be called back again, later. it's not recursive, and perfectly legal.
int ReadLen = t.Result;
if (ReadLen == 0)
{
Debug.WriteLine("Got a null read length, remote pipe was closed");
if (PipeClosedEvent != null)
{
PipeClosedEvent(this, new EventArgs());
}
return;
}
// lodge ANOTHER read request BEFORE calling the user back. Doing this ensures
// the read is ready before we call the user back, which may cause a write request to happen,
// which will zip over to the other end of the pipe, cause a write to happen THERE, and we won't be ready to receive it
// (perhaps it will stay stuck in a kernel queue, and it's not necessary to do this)
//
StartReadingAsync();
if (PipeReadDataEvent != null)
{
PipeReadDataEvent(this, new PipeReadEventArgs(pBuffer, ReadLen));
}
else
{
Debug.Assert(false, "something happened");
}
});
}
答案 1 :(得分:0)
您应该在异步模式下使用以下功能:
NamedPipeClientStream.BeginRead
NamedPipeClientStream.EndRead
和
NamedPipeServerStream.BeginWrite
NamedPipeServerStream.EndWrite
另外我确信WaitForPipeDrain()的正确实现总是在写入缓冲区之前使用,因为如果您之前已经写过,则需要检查是否已读取先前写入缓冲区。如果你编写然后使用WaitForPipeDrain(),那么后续调用你的写函数(甚至是错误的)将首先覆盖然后检查。我没有尝试过,但逻辑上可以认为是这样。