1

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

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

这有什么问题?

谢谢

4

2 回答 2

2

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

获取一个值,该值指示从最近的读取操作返回的消息中是否有更多数据。

否则namedPipeClient.IsMessageComplete返回true并且while-loop 内的代码不会被执行。因此,您必须重写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(),允许方法返回少于请求的数据(例如,当接收缓冲区中没有足够的空间来存储整个消息时,或者消息比请求的字节数短),请参阅文档

返回读入缓冲区的总字节数。如果该字节数当前不可用,则这可能小于请求的字节数,或者如果到达流的末尾,则为 0。

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

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

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

于 2018-09-01T08:04:10.463 回答
0

尝试让用户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();
}
于 2018-09-01T04:35:54.493 回答