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

时间:2010-06-04 15:16:57

标签: c# .net

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

设置为双向通信

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

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

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

显示客户端和服务器代码段:

SERVER:

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

4 个答案:

答案 0 :(得分:4)

尝试在流上设置Async标志:

NamedPipeClientStream(“。”,PipeName,PipeDirection.InOut,PipeOptions.Asynchronous);

答案 1 :(得分:2)

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

答案 2 :(得分:0)

我不确定这是否会有所帮助,但我也遇到了同样的问题。首先,我不知道为什么对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中有这么好的例子......

答案 3 :(得分:0)

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

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

服务器启动并侦听连接 - &gt;客户端启动与服务器的连接 - >服务器接受连接 - >客户端发出请求 - &gt;服务器发出响应 - &gt;连接已关闭。

enter image description here

服务器启动并侦听连接:

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是一个异步回调。此方法(位于客户端上)是管理Connection的位置:

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