命名管道流示例未显示结果

named pipe stream example not showing the result

我是管道流的新手并尝试练习。我写了以下两个项目,但看不到客户端项目的结果(我在服务器项目中写的)。这是第一个项目:

using (NamedPipeServerStream namedPipeServer = new NamedPipeServerStream("test-pipe", PipeDirection.InOut, 1, PipeTransmissionMode.Message))
{
    byte[] bytes = Encoding.Default.GetBytes("Hello, it's me!\n");
    namedPipeServer.WaitForConnection();
    Console.WriteLine("A client has connected!");
    namedPipeServer.Write(bytes, 0, bytes.Length);
}

这是第二个项目:

using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "test-pipe", PipeDirection.InOut))
{
    namedPipeClient.Connect();
    namedPipeClient.ReadMode = PipeTransmissionMode.Message;
    string serverResponse = string.Empty;
    byte[] readBytes = new byte[5];
    while (!namedPipeClient.IsMessageComplete)
    {
        namedPipeClient.Read(readBytes, 0, readBytes.Length);
        serverResponse = Encoding.Default.GetString(readBytes);
        readBytes = new byte[5];
    }
    System.Console.WriteLine(serverResponse);
    byte[] writeBytes = Encoding.Default.GetBytes("Hello from client!\n");
    namedPipeClient.Write(writeBytes, 0, writeBytes.Length);
}

这有什么问题?

谢谢

尝试用户 StreamReader 从两个管道读取消息。 服务器:

using (NamedPipeServerStream namedPipeServer = new NamedPipeServerStream("test-pipe", PipeDirection.InOut, 1, PipeTransmissionMode.Byte))
{
    namedPipeServer.WaitForConnection();
    Console.WriteLine("A client has connected!");

    byte[] bytes = Encoding.Default.GetBytes("Hello, it's me!\n");
    namedPipeServer.Write(bytes, 0, bytes.Length);
    namedPipeServer.WaitForPipeDrain();

    var reader = new StreamReader(namedPipeServer);
    var msg = reader.ReadLine();
    Console.WriteLine(msg);
}

客户:

using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "test-pipe", PipeDirection.InOut))
{
    namedPipeClient.Connect();

    var reader = new StreamReader(namedPipeClient);
    var msg = reader.ReadLine();
    Console.WriteLine(msg);

    byte[] writeBytes = Encoding.Default.GetBytes("Hello from client!\n");
    namedPipeClient.Write(writeBytes, 0, writeBytes.Length);
    namedPipeClient.WaitForPipeDrain();
}

客户端没有收到来自服务器的任何消息,因为 namedPipeClient.IsMessageComplete 在 读取操作后必须调用 。请参阅文档 PipeStream.IsMessageComplete

Gets a value indicating whether there is more data in the message returned from the most recent read operation.

否则 namedPipeClient.IsMessageComplete returns truewhile 循环内的代码不会被执行。因此,您必须将 while 循环重写为 do-while 循环,以确保在测试 namedPipeClient.IsMessageComplete 之前发生读取操作。

但问题较多,详见评论说明:

using (NamedPipeClientStream namedPipeClient = new NamedPipeClientStream(".", "test-pipe", PipeDirection.InOut))
{
    namedPipeClient.Connect();
    namedPipeClient.ReadMode = PipeTransmissionMode.Message;

    // StringBuilder is more efficient for string concatenation
    StringBuilder serverResponse = new StringBuilder();

    byte[] readBytes = new byte[5];

    do
    {
        // You need to store number of bytes read from pipe (to readCount variable).
        // It can be less then the length of readBytes buffer, in which case
        // GetString() would decode characters beyond end of message.
        var readCount = namedPipeClient.Read(readBytes, 0, readBytes.Length);
        var readText = Encoding.Default.GetString(readBytes, 0, readCount);

        // You original code "overwrites" content of serverResponse variable instead
        // of concatenating it to the previous value. So you would receive only 
        // the last part of the server message.
        serverResponse.Append(readText);

        // It is not needed to create new buffer, you can just reuse existing buffer
        //readBytes = new byte[5];

    // IsMessageComplete is now tested after read operation
    } while (!namedPipeClient.IsMessageComplete);

    System.Console.WriteLine(serverResponse.ToString());

    // You current server implementation exits as soon as it sends message to the client
    // and does not wait for incomming message. You'll have to change server accordingly 
    // to be able to send a message back to the server.
    //byte[] writeBytes = Encoding.Default.GetBytes("Hello from client!\n");
    //namedPipeClient.Write(writeBytes, 0, writeBytes.Length);
}

编辑:

当命名管道处于 PipeTransmissionMode.Message 模式时,服务器上对 NamedPipeServerStream.Write() 的每次调用都会将数据作为单独的消息通过管道发送。然后客户端可以接收这些彼此分开的消息 (与 PipeTransmissionMode.Byte 模式相反,客户端只接收单个连续的字节流,无论服务器使用 NamedPipeServerStream.Write() 执行了多少次写入)。

当客户端从管道 (namedPipeClient.Read()) 读取数据时,方法允许 return 少于请求的数据(例如,当接收缓冲区中没有足够的 space 来存储整个消息,或者消息比请求的字节数更短),请参阅 docs.

Returns the total number of bytes that are read into buffer. This might be less than the number of bytes requested if that number of bytes is not currently available, or 0 if the end of the stream is reached.

然后您可以使用 namedPipeClient.IsMessageCompletereadCount 来检测它。让我用一些例子来解释它:假设服务器向客户端发送消息 ABCDEFGHIJKL,编码为字节数组 { 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76 }。此消息的长度为 12 个字节,因此它不适合您的 5 个字节长的接收缓冲区 (readBytes)。因此,当客户端第一次使用 namedPipeClient.Read() 从管道读取时,接收缓冲区将仅包含消息的前 5 个字节({ 65, 66, 67, 68, 69 } 对应于 ABCDE)。而这正是 namedPipeClient.IsMessageComplete 可以帮助我们的地方,因为它会 return false 表明我们没有收到完整的消息,还有一些剩余字节,我们应该继续阅读。

第二次从管道读取类似,我们将读取消息的第二部分({ 70, 71, 72, 73, 74 }对应FGHIJ),namedPipeClient.IsMessageComplete仍然是false表示消息不完整。

当第三次从管道读取完成时,将只读取剩余的 2 个字节({ 75, 76 } 对应 KL),但是我们的缓冲区仍然有 5 个字节长,所以它看起来像这样: ({ 75, 76, 72, 73, 74 }对应KLHIJ)。值 72, 73, 74 仍然存在于上一次循环迭代中。现在重要的是将值 returned 从 namedPipeClient.Read() 存储到 readCount 变量。它将包含值 2,表示 bytesRead 缓冲区中只有 2 个字节有效,其余字节应被忽略。