无法通过 NamedPipeServerStream 和 NamedPipeClientStream 获取字符串数据

Posted

技术标签:

【中文标题】无法通过 NamedPipeServerStream 和 NamedPipeClientStream 获取字符串数据【英文标题】:Cannot get string data to pass through NamedPipeServerStream and 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 从来没有返回? 【参考方案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

【讨论】:

以上是关于无法通过 NamedPipeServerStream 和 NamedPipeClientStream 获取字符串数据的主要内容,如果未能解决你的问题,请参考以下文章

oracle 关于无法通过128 表无法扩展

无法通过 PancakeSwap 路由器移除流动性。通过“写”合约成功添加,但无法删除LiquidityETH

为啥 charles 无法通过 c++ 捕获 http 请求?

Xampp 无法创建数据库,也无法通过 Phpmyadmin 运行查询

无法通过单击按钮(仅通过手势)打开导航抽屉

无法通过 Mandrill 发送密件抄送电子邮件(通过 Laravel)