0

我想出了以下代码:

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

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

4

2 回答 2

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


        });
    }
于 2016-01-06T04:35:19.240 回答
1

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

NamedPipeClientStream.BeginRead
NamedPipeClientStream.EndRead

NamedPipeServerStream.BeginWrite
NamedPipeServerStream.EndWrite

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

于 2014-12-21T07:52:17.400 回答