-2

I am working with named pipes and very confused about how the interaction between the client and server is happening. My server is a VM with a named pipe attached to it. I have created a client stream in C# this way:

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

Let's say the VM prompt is like below:

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

What I essentially need to do is, check if the line read by the stream is "Do you accept (y/n):", if it matches then write y to the stream.es with Text1, then write 1.2.3.4 in the stream

Problems I am facing:

  1. The prompt doesn't display anything after Hello!. What I thought is, it is maybe waiting for the next input. So, instead of doing this:

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

    I did this:

    if(line.contains("Hello!"))
         writer.writeLine("y");
    
  2. Is this the correct thing? If it is, then it means that the server isn't pushing the text in the buffer on which it is awaiting input. So, every time I'll have to do the check for the previous line and write the output of the next expected line to the writer stream?

  3. Also, as per above, for passing 1.2.3.4 for Text1, I did this:

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

    What appears in my execution prompt is: Text1 : 1111111 ie. it's only repeating the first character of the input I have passed. Why such? I have already set AutoFlush = true to my writer stream.

Any pointers here will be highly appreciated!

Edit: Including the snippet

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

Is above the right way? Also, sometimes the pipe reader is missing data. Like it shows Tet1 instead of Text1. Why such?

user3552407
  • 361
  • 1
  • 4
  • 14
  • 1
    It sounds a little bit too broad, what exactly is the question or problem? – Progman Apr 15 '18 at 08:34
  • @3vts The first question is the point 2 mentioned above. Is it expected that data at the prompt (like Text1: or Text2:) will not be pushed in the buffer by the server till it gets the input and that's why I'm unable to read it? – user3552407 Apr 15 '18 at 09:44
  • And then the second ques is the point 3 mentioned above, why if I pass 1.2.3.4, I only see 1111111. – user3552407 Apr 15 '18 at 09:44
  • You have to edit your question to include the source code you have. Keep in mind to post a [MCVE](https://stackoverflow.com/help/mcve), which shows your problem only. – Progman Apr 15 '18 at 10:36
  • @Progman Edited. – user3552407 Apr 15 '18 at 11:53
  • It looks like this is only the client code. Please add the code of the server as well. And include the full source code, including the class name and `using` statements. – Progman Apr 15 '18 at 12:31
  • @Progman Edited the code above. Also, there's no code for the server as I mentioned that the server is a VM (created using Hyper-V) with a named pipe attached to it. – user3552407 Apr 15 '18 at 12:36

1 Answers1

2

You have this statement: while(!client.IsConnected); which causes the client to wait for the connection before proceeding. However, the client never attempts to connect (client.Connect();), so presumably you will be waiting forever. Replace that line with if (!client.IsConnected) {client.Connect();} to get this to work.

Beyond that, per the comments, without knowing what your server side code looks like it's hard to say what the client should be sending; as the two sides are having a scripted conversation.

Here's an example of some working server & client code to illustrate how you could perform this conversation:

Code to kick off both client & server:

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

Client code:

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

Server code:

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

Update

Amended code to read characters rather than lines; so we don't have to wait for a line break before seeing the server's messages.

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

So this is what your code should look like:

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;
            }
        }
    }
}
JohnLBevan
  • 22,735
  • 13
  • 96
  • 178
  • Thanks for your detailed answer. Client.connect() is there in my code, just forgot to copy that in the above snippet. Also, as I mentioned that I my server is a virtual machine which I have setup using hyper-v. Then I have attached the named pipe with this server. So, there'll be no code for server in my case. Let me know if I'm wrong. – user3552407 Apr 15 '18 at 16:25
  • No worries. WHen you say there is no code for the server, what are you communicating with; i.e. if the server code's not something you've written yourself presumably you're connecting with some known service... What service is that / what's playing the role of the server here / what is your client trying to talk to? i.e. if it's not something you've written we'll need to find the documentation for that service so we know how that expects to interact. – JohnLBevan Apr 15 '18 at 16:29
  • Imagine you are on Windows machine. You create let's say, a VM using Hyper-v using the VHDK you have. Now you go and attach a named pipe to it. Now, you use putty to establish a serial communication. So, whatever data comes on VM's serial port will be seen in Putty's session window. So, I need not write any code for the server. I hope this clarifies the ques a little more. – user3552407 Apr 15 '18 at 16:44
  • So, now in this case, let's say the data on console port of VM comes "Hello", the client code I mentioned above reads that perfectly. But let's say the next line says "text1: " the client code doesn't display, even text1:. But if the writer already has input in its buffer, the client code says text1: . And this confuses as to why is this such – user3552407 Apr 15 '18 at 16:49
  • Sorry @user3552407, I'm not sure I understand the issue (or rather, I think I get the issue, but don't know enough about its context to comment)... What's your objective; i.e. why are you connecting to this named pipe in the first place; that may give me the clue to the context I'm after... – JohnLBevan Apr 15 '18 at 22:06
  • Because I want to parse the console data in my code. An when my code shall parse text1:, I need to send the input via my code to the named pipe. – user3552407 Apr 16 '18 at 03:34
  • Still not sure how you're connecting to the server's console; but maybe that's my lack of knowledge of that area. However, I think I've figured out the issue... maybe the server's doing a `writer.Write("Do you accept (y/n):");` instead of a `writer.WriteLine("Do you accept (y/n):");`; so the client's `ReadLine` would be waiting for the line to complete before returning a response. Using the client's `Read` instead to get 1 character at a time and build up the string we can see the full communication. – JohnLBevan Apr 16 '18 at 07:11