【问题标题】:Cannot get string data to pass through NamedPipeServerStream and NamedPipeClientStream无法通过 NamedPipeServerStream 和 NamedPipeClientStream 获取字符串数据
【发布时间】:2010-06-04 15:16:56
【问题描述】:

我正在尝试使用两个简单的 C# 表单解决方案在我的 Win-XP 工作站上实现双向命名管道通信。一个用于客户端,一个用于服务器。它们看起来几乎相同,并使用 NamedPipeServerStream 和 NamedPipeClientStream (.NET 3.5)。客户端和服务器都通过 PipeDirection.InOut

设置为双向通信

启动事件的顺序是: 1)启动服务器。它等待来自客户端的连接。 2)启动客户端,它立即找到并连接到服务器。同样,服务器完成与客户端的连接。 3)客户端和服务器都启动它们的“读取”线程,从而创建流读取器的实例。然后这些线程调用 ReadLn() 并阻塞 - 等待数据。在所有情况下,自动刷新都是正确的。

然后我使用 streamwriter.WriteLn() 将字符串数据从服务器发送到客户端(反之亦然)。但是,执行永远不会从该调用返回。我不知道为什么,任何见解都会受到欢迎。

我花了相当多的时间研究有关这个主题的所有内容,但我仍然缺少一些东西。

客户端和服务端代码sn-ps如图:

服务器:

   private void ListenForClients()
    {
        // Only one server as this will be a 1-1 connection
        m_pipeServerStream = new NamedPipeServerStream(PipeName, PipeDirection.InOut, 1);

        // Wait for a client to connect
        m_pipeServerStream.WaitForConnection();

        // Ccould not create handle - server probably not running
        if (!m_pipeServerStream.IsConnected)
            return;

        // Create a stream writer which flushes after every write
        m_pipeServerWriter = new StreamWriter(m_pipeServerStream);
        m_pipeServerWriter.AutoFlush = true;

        Connected = true;

        // Start listening for messages
        if (m_pipeServerStream.CanRead)
        {
            ReadThread = new Thread(new ParameterizedThreadStart(Read));
            ReadThread.Start(m_pipeServerStream);
        }
    }

    /// <summary>
    /// Reads data from the client
    /// </summary>
    /// <param name="serverObj"></param>
    private void Read(object serverObj)
    {
        NamedPipeServerStream pipeStream = (NamedPipeServerStream)serverObj;
        using (StreamReader sr = new StreamReader(pipeStream))
        {
            while (true)
            {
                string buffer;
                try
                {
                    buffer = sr.ReadLine();
                }
                catch
                {
                    //read error has occurred
                    break;
                }

                //client has disconnected
                if (buffer.Length == 0)
                    break;

                //fire message received event
                if (MessageReceived != null)
                {
                    MessageReceived(buffer);
                }
            }
        }
    }

    /// <summary>
    /// Sends a message to the connected client
    /// </summary>
    /// <param name="message">the message to send</param>
    public void SendMessage(string message)
    {
        if (m_pipeServerWriter != null)
        {
            m_pipeServerWriter.WriteLine(message);
            m_pipeServerWriter.Flush();
        }
    }

客户:

   private void ConnectToServer()
    {
        // Seek out the one server
        m_pipeClientStream = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut);

        // Connect to the waiting server
        m_pipeClientStream.Connect();

        // Ccould not create handle - server probably not running
        if (!m_pipeClientStream.IsConnected)
            return;

        // Create a stream writer which flushes after every write
        m_pipeClientWriter = new StreamWriter(m_pipeClientStream);
        m_pipeClientWriter.AutoFlush = true;

        Connected = true;

        // Start listening for messages
        if (m_pipeClientStream.CanRead)
        {
            ReadThread = new Thread(new ParameterizedThreadStart(Read));
            ReadThread.Start(m_pipeClientStream);
        }
    }

    /// <summary>
    /// Reads data from the server
    /// </summary>
    private void Read(object serverObj)
    {
        NamedPipeClientStream pipeStream = (NamedPipeClientStream)serverObj;
        using (StreamReader sr = new StreamReader(pipeStream))
        {
            while (true)
            {
                string buffer;
                try
                {
                    buffer = sr.ReadLine();
                }
                catch
                {
                    //read error has occurred
                    break;
                }

                //client has disconnected
                if (buffer.Length == 0)
                    break;

                //fire message received event
                if (MessageReceived != null)
                {
                    MessageReceived(buffer);
                }
            }
        }
    }

    /// <summary>
    /// Sends a message to the connected server
    /// </summary>
    /// <param name="message"></param>
    public void SendMessage(string message)
    {
        if (m_pipeClientWriter != null)
        {
            m_pipeClientWriter.WriteLine(message);
            m_pipeClientWriter.Flush();
        }
    }

【问题讨论】:

  • 你有没有弄清楚为什么 Write 从来没有返回?

标签: c# .net


【解决方案1】:

尝试在流上设置异步标志:

NamedPipeClientStream(".", PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);

【讨论】:

    【解决方案2】:

    我现在已经放弃并转向使用两个管道的安全、明显的技术,一个用于通信的每个方向。它们工作正常。

    【讨论】:

      【解决方案3】:

      我不确定这是否会有所帮助,但我也遇到了同样的问题。首先,我不知道为什么对 m_pipeServerStream.IsConnected 的任何引用都会破坏管道。我只用一个简单的 MessageBox.Show(m_pipeServerStream.IsConnected.ToString()) 对此进行了测试,这打破了我的管道!

      其次,另一个奇怪的事情是,如果您使用的是双工命名管道,您的 streamreader 调用将永远不会返回。你需要像这样手动阅读它

          const int BufferSize = 4096;
          Decoder decoder = Encoding.UTF8.GetDecoder();
          StringBuilder msg = new StringBuilder();
          char[] chars = new char[BufferSize];
          byte[] bytes = new byte[BufferSize];
          int numBytes = 0;
          MessageBox.Show("before do while loop");
          numBytes = pipeServer.Read(bytes, 0, BufferSize);
          if (numBytes > 0)
          {
              int numChars = decoder.GetCharCount(bytes, 0, numBytes);
              decoder.GetChars(bytes, 0, numBytes, chars, 0, false);
              msg.Append(chars, 0, numChars);
          }
      
          MessageBox.Show(numBytes.ToString() + " " + msg.ToString());
      
          MessageBox.Show("Finished reading, now starting writing");
          using (StreamWriter swr = new StreamWriter(pipeServer))
          {
              MessageBox.Show("Sending ok back");
              swr.WriteLine("OK");
              pipeServer.WaitForPipeDrain();
          }
      

      无论如何,它似乎不喜欢 StreamReader 的行为,但这暂时可以使用......我从这个链接 http://social.msdn.microsoft.com/forums/en-US/csharpgeneral/thread/23dc2951-8b59-48e4-89fe-d2b435db48c6/ 得到了这个

      我没有跟踪每一步,因为我只需要找出它为什么一直挂在 StreamReader.ReadLine() 上。它不是从这个函数返回的。 StreamWriter 似乎没有这个问题。

      我实际上是在本机 dll 和托管 Windows 服务之间进行通信。想象一下,当我发现问题出在托管部分而不是非托管部分时,我感到很惊讶,因为他们在 msdn 中有很好的示例......

      【讨论】:

        【解决方案4】:

        我不是命名管道或匿名管道方面的专家,但我会尽我最大的努力帮助他人,即使你已经解决了你的问题。

        客户端服务器通信是思考如何实现此过程的最佳方式。

        服务器启动并侦听连接 --> 客户端发起到服务器的连接 -->服务器接受连接 -->客户端发出请求 -->服务器做出响应 --> 连接关闭.

        服务器启动并监听连接:

        try
        {
        namedPipeServerStream = new NamedPipeServerStream(PipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
        
        // Wait for a connection here...
        namedPipeServerStream.BeginWaitForConnection(new AsyncCallback(ConnectionCallBack), namedPipeServerStream);
        }
        catch (Exception ex)
        {
        Debug.WriteLine(ex.Message);
        }
        

        客户端连接,然后发出请求:

        try
        {
        namedPipeClientStream = new NamedPipeClientStream(".", PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
        
        // Connect with timeout...
        namedPipeClientStream.Connect(TimeOut);
        
        byte[] buffer = Encoding.UTF8.GetBytes(DataToSend);
        namedPipeClientStream.BeginWrite(buffer, 0, buffer.Length, ConnectionCallBack, namedPipeClientStream);
        }
        catch (TimeoutException ex)
        {
        Debug.WriteLine(ex.Message);
        }
        

        ConnectionCallBack 是一个异步回调。这个方法(在客户端)是管理连接的地方:

        private void ConnectionCallBack(IAsyncResult iAsyncResult)
        {
        try
        {
        // Get the pipe
        NamedPipeClientStream namedPipeClientStream = (NamedPipeClientStream)iAsyncResult.AsyncState;
        
        // End the write
        namedPipeClientStream.EndWrite(iAsyncResult);
        namedPipeClientStream.Flush();
        
        // Get Server Response...
        GetServerResponse(namedPipeClientStream);
        
        // Flush Data and Close Pipe...
        namedPipeClientStream.Flush();
        namedPipeClientStream.Close();
        namedPipeClientStream.Dispose();
        }
        catch (Exception ex)
        {
        Debug.WriteLine(ex.Message);
        }
        }
        

        服务器处理客户端请求并制定响应并发送:

        // Response Methods...
        public void SendResponse(string ServerResponse)
        {
        try
        {
        // Fill Buffer with Server Response Data...
        byte[] Buffer = Encoding.UTF8.GetBytes(ServerResponse);
        
        // Begin Async Write to the Pipe...
        namedPipeServerStream.BeginWrite(Buffer, 0, Buffer.Length, SendResponseCallBack, namedPipeServerStream);
        }
        catch (Exception ex)
        {
        Debug.WriteLine(ex.Message);
        }
        }
        
        private void SendResponseCallBack(IAsyncResult iAsyncResult)
        {
        try
        {
        // Get the Pipe Handle...
        NamedPipeServerStream namedPipeServerStream = (NamedPipeServerStream)iAsyncResult.AsyncState;
        
        // End the Write and Flush...
        namedPipeServerStream.EndWrite(iAsyncResult);
        namedPipeServerStream.Flush();
        
        // Close the Connection and Dispose...
        namedPipeServerStream.Close();
        namedPipeServerStream.Dispose();
        }
        catch (Exception ex)
        {
        Debug.WriteLine(ex.Message);
        }
        }
        

        这是从客户端请求处理程序调用的:

        private void ClientRequestHandler(string clientRequest)
        {
        try
        {
        if (this.InvokeRequired)
        {
        this.Invoke(new InvokedDelegate(ClientRequestHandler), clientRequest);
        }
        else
        {
        ProcessClientRequest(clientRequest);
        }
        }
        catch (Exception ex)
        {
        Debug.WriteLine(ex.Message);
        }
        }
        
        private void ProcessClientRequest(string clientRequest)
        {
        // Display the Client Request...
        richTextBox1.Text = clientRequest;
        
        PipeServer.SendResponse("Server has received Client Request at: " + DateTime.Now);
        }
        

        客户端已在异步回调方法看到的点处发起与服务器的连接:

        // Get Server Response...
        GetServerResponse(namedPipeClientStream);
        

        连接仍然打开。客户端请求已发出,管道已刷新,客户端已准备好读取上述服务器响应:

        private void GetServerResponse(NamedPipeClientStream namedPipeClientStream)
        {
        byte[] buffer = new byte[255];
        namedPipeClientStream.Read(buffer, 0, buffer.Length);
        
        // Convert byte buffer to string
        string ResponseData = Encoding.UTF8.GetString(buffer, 0, buffer.Length);
        
        // Pass message back to calling form
        ServerResponse.Invoke(ResponseData);
        }
        

        收到响应,然后再次刷新和关闭连接,以便客户端启动另一个连接。

        代码比这个复杂一点,但本质上它是这样工作的。当您启动连接时,请使用它。一旦你关闭它,然后尝试重新初始化它,你将需要等待一段时间才能正确处理它,否则你会得到各种信号量错误等等。不需要时不要吸烟!!!

        请看:Code Project - C# Async Named Pipes for an excellent example

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-10-21
          • 1970-01-01
          • 2019-07-05
          • 1970-01-01
          • 2018-12-31
          • 2016-09-03
          • 1970-01-01
          • 2020-08-14
          相关资源
          最近更新 更多