具有命名管道的异步服务器和客户端

时间:2013-09-09 07:12:21

标签: c# .net asynchronous ipc pipe

我想出了以下代码:

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)
        {
        }
    }
}

这是管道通信解决方案的客户端服务器。但是我需要服务器以异步方式写入消息,并且客户端在弹出时也会异步地读取它们。我怎样才能做到这一点?有大量的样本,但大多数都考虑客户写入服务器,我不知道如何实现我的目标,特别是我已编写的代码...

2 个答案:

答案 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(),那么后续调用你的写函数(甚至是错误的)将首先覆盖然后检查。我没有尝试过,但逻辑上可以认为是这样。