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

C# 具有命名管道的异步服务器和客户端,c#,.net,asynchronous,ipc,pipe,C#,.net,Asynchronous,Ipc,Pipe,我想出了以下代码: class IPCServer { private Thread ipcServerThread; private NamedPipeServerStream pipeServer; public IPCServer() { pipeServer = new NamedPipeServerStream("iMedCallInfoPipe", PipeDirection.Out, 1, PipeTransmissionMode.

我想出了以下代码:

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

这是管道通信解决方案的客户机服务器。但是我需要服务器异步地写消息,而客户端在消息弹出时也异步地读取它们。我怎样才能做到这一点?虽然有很多样本,但大多数都考虑客户端写入服务器,我不知道如何实现我的目标,尤其是我已经编写的代码…

异步模式下应该使用以下函数:

NamedPipeClientStream.BeginRead
NamedPipeClientStream.EndRead


此外,我确信在写入缓冲区之前,总是使用WaitForPipeDrain()的正确实现,因为如果您以前写入过缓冲区,则需要检查之前写入缓冲区的内容是否已被读取。如果您先编写,然后使用WaitForPipeDrain(),那么对写入函数的后续调用(甚至错误地)将首先覆盖,然后检查。我没有尝试过,但从逻辑上讲,可以这样认为。

在异步模式下,您应该使用以下功能:

NamedPipeClientStream.BeginRead
NamedPipeClientStream.EndRead


此外,我确信在写入缓冲区之前,总是使用WaitForPipeDrain()的正确实现,因为如果您以前写入过缓冲区,则需要检查之前写入缓冲区的内容是否已被读取。如果您先编写,然后使用WaitForPipeDrain(),那么对写入函数的后续调用(甚至错误地)将首先覆盖,然后检查。我没有试过,但从逻辑上讲,可以这样认为。

首先,你不需要两个管道。。您可以在一个命名管道的两端进行写/读操作,这样它就不会得到消息或顺序了。它也不会被卡住。只要你遵守基本的管道规则,双方都可以随心所欲地快速书写,也可以随时阅读

其次,您应该使用消息模式,而不是字节模式,否则它可能会将发送或读取的消息合并到一个缓冲区中

第三,我非常确定,如果使用基于消息的管道,而不是字节,则不需要调用WaitForPipeDrain或Flush。我想这只会让你不必要地慢下来。如果我错了,有人在下面纠正我吗

最后,您可以通过以下方式“随时”在客户机上获取读取:始终保持异步读取。也就是说,一旦客户机连接到服务器,立即对给定的缓冲区执行异步读取,您知道该缓冲区“足够大”,可以容纳您可能得到的任何内容。当您的异步读取回调被调用(您提供给异步读取方法的回调)时,您将被告知缓冲区已被填充了多少。然后,您可以向客户机类的用户引发事件(您定义的事件),或者调用回调,或者执行一些操作。完成事件回调后,立即执行另一个异步读取。如果没有什么可读的,那没关系,它会在那里等着有东西进来。请注意,如果异步读取始终挂起,当远程管道关闭时,异步读取将以“管道关闭”和“0字节读取”返回给您。这是正常的

下面是我的read方法的一个片段,在我的客户端类中:

  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");
            }


        });
    }

首先,你不需要两根管子。。您可以在一个命名管道的两端进行写/读操作,这样它就不会得到消息或顺序了。它也不会被卡住。只要你遵守基本的管道规则,双方都可以随心所欲地快速书写,也可以随时阅读

其次,您应该使用消息模式,而不是字节模式,否则它可能会将发送或读取的消息合并到一个缓冲区中

第三,我非常确定,如果使用基于消息的管道,而不是字节,则不需要调用WaitForPipeDrain或Flush。我想这只会让你不必要地慢下来。如果我错了,有人在下面纠正我吗

最后,您可以通过以下方式“随时”在客户机上获取读取:始终保持异步读取。也就是说,一旦客户机连接到服务器,立即对给定的缓冲区执行异步读取,您知道该缓冲区“足够大”,可以容纳您可能得到的任何内容。当您的异步读取回调被调用(您提供给异步读取方法的回调)时,您将被告知缓冲区已被填充了多少。然后,您可以向客户机类的用户引发事件(您定义的事件),或者调用回调,或者执行一些操作。完成事件回调后,立即执行另一个异步读取。如果没有什么可读的,那没关系,它会在那里等着有东西进来。请注意,如果异步读取始终挂起,当远程管道关闭时,异步读取将以“管道关闭”和“0字节读取”返回给您。这是正常的

下面是我的read方法的一个片段,在我的客户端类中:

  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");
            }


        });
    }

听起来你需要两个客户端和两个服务器。你为什么这么认为?我只需要服务器写入管道,客户端读取管道,但我不想让它循环,所以异步方法听起来是个更好的主意。但我不确定如何在服务器中实现BeginWrite和在客户端实现BeginRead。您不使用WCF有什么特殊原因吗?在我看来,客户端和服务器角色可能会颠倒:通常服务器角色会响应来自客户端角色的事件。也许客户端(如图所示)应该使用PipeDirection打开一个管道。在和中,服务器(如图所示)应该在服务器生成事件时打开并写入管道。听起来您需要两个客户端和两个服务器。为什么这样认为?我只需要服务器写入管道,客户端读取管道,但我不想让它循环,所以异步方法听起来是个更好的主意。但我不确定如何在服务器中实现BeginWrite和在客户端实现BeginRead。您不使用WCF有什么特殊原因吗?在我看来,客户端和服务器角色可能会颠倒:通常服务器角色会响应来自客户端角色的事件。也许客户端(如图所示)应该使用PipeDirection.In打开一个管道,而服务器(如图所示)应该使用sh