C# 中的命名管道输入输出

Posted

技术标签:

【中文标题】C# 中的命名管道输入输出【英文标题】:Named Pipe Input Output in C# 【发布时间】:2018-04-15 04:59:00 【问题描述】:

我正在使用命名管道,并且对客户端和服务器之间的交互如何发生感到非常困惑。 我的服务器是一个附加了命名管道的虚拟机。我以这种方式在 C# 中创建了一个客户端流:

NamedPipeClientStream client = new NamedPipeClientStream(".", "TestPipe", PipeDirection.InOut);

假设VM提示如下:

Hello!
Do you accept (y/n): y
Text1 : 1.2.3.4
Text2 : 2.3.4.5

我基本上需要做的是,检查流读取的行是否是“你接受(y/n):”,如果匹配,则使用 Text1 将 y 写入 stream.es,然后写入 1.2。 3.4 在流中

我面临的问题:

    Hello! 之后的提示不显示任何内容。我的想法 是,它可能正在等待下一个输入。所以,而不是做 这个:

    if(line.contains("Do you accept (y/n):"))
           writer.writeLine("y")
    

    我这样做了:

    if(line.contains("Hello!"))
         writer.writeLine("y");
    

    这是正确的吗?如果是,则表示服务器 没有将文本推送到等待输入的缓冲区中。 所以,每次我都必须检查前一行和 将下一个预期行的输出写入写入器流?

    另外,如上所述,为了将1.2.3.4 传递给Text1,我这样做了:

    if(line.contains("Do you accept (y/n):"))
           writer.writeLine("1.2.3.4")
    

    我的执行提示中出现的是:Text1 : 1111111 即。它是 只重复我通过的输入的第一个字符。为什么 这样的?我已经将AutoFlush = true 设置为我的作家流。

这里的任何指针将不胜感激!

编辑:包括sn-p

    using System;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Windows;


namespace App1 
    static void Main(string [] args) 
          NamedPipeClientStream client = new NamedPipeClientStream(".", "TestPipe", PipeDirection.InOut);
       while(!client.IsConnected);
       Streamreader reader = new StreamReader(client);
       StreamWriter writer = new StreamWriter(client);
       string line = "";
       writer.AutoFlush = true;
       while((line = reader.readLine()) != null) 
          if(line.Contains("Hello!")  
            writer.writeLine("1.2.3.4"); // input for the next prompt
          
          else if(line.Contains("Text1") 
            writer.writeLine("2.3.4.5"); // input for next prompt of Text2
          
          writer.Flush();
          writer.WaitForPipeDrain();
       
    

上面是正确的方式吗? 此外,有时管道读取器会丢失数据。就像它显示 Tet1 而不是 Text1。为什么会这样?

【问题讨论】:

听起来有点太笼统了,到底是什么问题? @3vts 第一个问题就是上面提到的第2点。是否预计提示符处的数据(如 Text1: 或 Text2:) 在收到输入之前不会被服务器推送到缓冲区中,这就是我无法读取它的原因? 然后第二个问题就是上面提到的第3点,为什么如果我通过1.2.3.4,我只看到1111111。 您必须编辑您的问题以包含您拥有的源代码。请记住发布MCVE,它只显示您的问题。 @Progman 已编辑。 【参考方案1】:

您有这样的语句:while(!client.IsConnected);,这会导致客户端在继续之前等待连接。但是,客户端从不尝试连接 (client.Connect();),因此您可能会一直等待。将该行替换为 if (!client.IsConnected) client.Connect(); 以使其正常工作。

除此之外,根据 cmets,如果不知道您的服务器端代码是什么样的,就很难说客户端应该发送什么;因为双方正在进行脚本对话。

以下是一些工作服务器和客户端代码的示例,用于说明如何执行此对话:

启动客户端和服务器的代码:

static void Main()

    var server = Task.Factory.StartNew(() => RunServer());
    var client = Task.Factory.StartNew(() => RunClient());
    Task.WaitAll(server, client);
    Console.WriteLine("Done");

客户端代码:

static void RunClient()

    using (var pipeClient = new NamedPipeClientStream(".", "TestPipe", PipeDirection.InOut))
    
        Console.WriteLine("Client is waiting to connect");
        if(!pipeClient.IsConnected)pipeClient.Connect();
        Console.WriteLine("Client is connected");
        using (var reader = new StreamReader(pipeClient))
        
            using (var writer = new StreamWriter(pipeClient))
            
                var running = true;
                while(running) 
                
                    Console.WriteLine("Client is waiting for input");
                    var message = reader.ReadLine();
                    if (message != null)
                    
                        Console.WriteLine("Client: Recieved from server 0", message);
                        switch (message)
                        
                            case "Do you accept (y/n):":
                                writer.WriteLine("y");
                                writer.WriteLine("quit");
                                writer.Flush();
                                break;
                            case "quit": 
                                running = false; 
                                break;
                        
                    
                 
            
        
    
    Console.WriteLine("Client Quits");

服务器代码:

static void RunServer()

    using (var pipeServer = new NamedPipeServerStream("TestPipe", PipeDirection.InOut))
    
        using (var reader = new StreamReader(pipeServer))
        
            using (var writer = new StreamWriter(pipeServer))
            
                var running = true;
                Console.WriteLine("Server is waiting for a client");
                pipeServer.WaitForConnection();
                Console.WriteLine("Server has connection from client");
                Console.WriteLine("Server: Saying Hi");
                writer.WriteLine("Hello!");
                Console.WriteLine("Server: Prompting for Input");
                writer.WriteLine("Do you accept (y/n):");
                writer.Flush();
                while(running) 
                
                    pipeServer.WaitForPipeDrain();
                    var message = reader.ReadLine();
                    Console.WriteLine("Server: Recieved from client 0", message);
                    if (message.Equals("quit")) 
                    
                        writer.WriteLine("quit");
                        running = false;
                    
                 
            
        
    
    Console.WriteLine("Server Quits");


更新

修改代码以读取字符而不是行;所以我们不必等待换行符才能看到服务器的消息。

//using System.Threading.Tasks;
//using System.IO.Pipes;

static void Main()

    var server = Task.Factory.StartNew(() => RunServer());
    var client = Task.Factory.StartNew(() => RunClient());
    Task.WaitAll(server, client);
    Console.WriteLine("Done");


static void RunClient()

    using (var pipeClient = new NamedPipeClientStream(".", "TestPipe", PipeDirection.InOut))
    
        Console.WriteLine("Client is waiting to connect");
        if(!pipeClient.IsConnected)pipeClient.Connect();
        Console.WriteLine("Client is connected");
        using (var reader = new StreamReader(pipeClient))
        
            using (var writer = new StreamWriter(pipeClient))
            
                var message = string.Empty;
                var running = true;
                while(running) 
                
                    Console.WriteLine("Client is waiting for input");
                    var chr = reader.Read();
                    if (chr >= 32)
                    
                        message = message + (char)chr;
                        Console.WriteLine("Client: Recieved from server 0", message);
                        switch (message)
                        
                            case "Do you accept (y/n):":
                                writer.WriteLine("y");
                                writer.WriteLine("quit");
                                writer.Flush();
                                break;
                            case "quit": 
                                running = false; 
                                break;
                        
                    
                    else 
                        message = string.Empty;
                        Console.WriteLine("Client: New Line Received from Server");
                    
                 
            
        
    
    Console.WriteLine("Client Quits");


static void RunServer()

    using (var pipeServer = new NamedPipeServerStream("TestPipe", PipeDirection.InOut))
    
        using (var reader = new StreamReader(pipeServer))
        
            using (var writer = new StreamWriter(pipeServer))
            
                var running = true;
                Console.WriteLine("Server is waiting for a client");
                pipeServer.WaitForConnection();
                Console.WriteLine("Server has connection from client");
                Console.WriteLine("Server: Saying Hi");
                writer.WriteLine("Hello!");
                Console.WriteLine("Server: Prompting for Input");
                writer.Write("Do you accept (y/n):"); //NB: This is a write, not a write line!
                writer.Flush();
                while(running) 
                
                    pipeServer.WaitForPipeDrain();
                    var message = reader.ReadLine();
                    Console.WriteLine("Server: Recieved from client 0", message);
                    switch (message)
                    
                        case "quit":  
                            writer.WriteLine("quit");
                            running = false;
                            break;
                        default:
                            writer.WriteLine("");
                            break;
                     
                
            
        
    
    Console.WriteLine("Server Quits");


所以你的代码应该是这样的:

int charAsInt;
var line = string.Empty;
while(((charAsInt = reader.Read()) != -1)) 
    if (charAsInt >= 32)  //is a displayable character
        line = line + (char)charAsInt;

        //your code to handle the lines
        if(line.Contains("Hello!")) 
            writer.WriteLine("1.2.3.4"); // input for the next prompt
         else if(line.Contains("Text1") 
            writer.WriteLine("2.3.4.5"); // input for next prompt of Text2
        
        writer.Flush();
        //writer.WaitForPipeDrain();

     else  //is a control character
        if (charAsInt == 10 || charAsInt == 13)  //carriage return or line feed; i.e. end of line
            if (line.Length > 0) 
                Debug.WriteLine("Last line read was 0", line); //just so you can see info as it comes from the server
                line = string.Empty;
            
        
    

【讨论】:

感谢您的详细回答。 Client.connect() 在我的代码中,只是忘记在上面的 sn-p 中复制它。另外,正如我提到的,我的服务器是我使用 hyper-v 设置的虚拟机。然后我已将命名管道与此服务器相连。因此,在我的情况下,将没有服务器代码。如果我错了,请告诉我。 不用担心。当您说服务器没有代码时,您在与什么通信;即,如果服务器代码不是您自己编写的,大概您正在连接一些已知的服务......那是什么服务/这里的服务器在扮演什么角色/您的客户试图与之交谈?即,如果它不是你写的东西,我们需要找到该服务的文档,以便我们知道它期望如何交互。 想象一下你在 Windows 机器上。假设您使用 Hyper-v 使用您拥有的 VHDK 创建了一个 VM。现在你去给它附加一个命名管道。现在,您使用 putty 建立串行通信。因此,无论 VM 串行端口上的数据如何,都将在 Putty 的会话窗口中看到。所以,我不需要为服务器编写任何代码。我希望这能进一步澄清问题。 所以,现在在这种情况下,假设VM控制台端口上的数据是“Hello”,我上面提到的客户端代码完美地读取了这一点。但是假设下一行显示“text1: ”客户端代码不显示,甚至 text1:。但是如果作者的缓冲区中已经有输入,客户端代码会显示 text1: 。这令人困惑,为什么会这样 对不起@user3552407,我不确定我是否理解这个问题(或者更确切地说,我认为我明白了这个问题,但对它的上下文了解不够多,无法发表评论)......你的目标是什么;即你为什么首先连接到这个命名管道;这可能会给我提供我所追求的上下文的线索......

以上是关于C# 中的命名管道输入输出的主要内容,如果未能解决你的问题,请参考以下文章

C#与C互传数据

二.编写第一个c#程序(注释,命名空间,类,Main方法,标识符,关键字,输入,输出语句,)

☀️ 学会编程入门必备 C# 最基础知识介绍——接口命名空间预处理指令正则表达式异常处理文件的输入与输出

TIJ -- 任务间使用管道进行输入/输出

linux中的管道符重定向于环境变量

了解下C# 文件的输入与输出