How to stop console.readline from hacking during console.writeline in a streaming application?

I recently started work on a multi-threaded console application using VB.Net, and I have a (understandable) error. The program itself is a server that receives socket connections from external applications in different threads. I also have a separate thread that just waits for the user to enter text into the application using console.readline (so that you can enter server commands such as HELP or STATUS).

The problem is that the program displays messages from different socket streams, and if I am in the middle of the command input, it splits. If I enter the "status" command, it works as it should, but for the user entering the command, it breaks and they may not see if they typed the command incorrectly. For instance:

New client installed.
EP

I know why this is happening, but I'm curious if there is a simple fix to stop it (without having to pause other threads during input) or, perhaps, an easier way to allow console commands that will not be moved to additional application output.

+4
source share
5 answers

I know that this problem is mostly aesthetic, but it annoys me. I hate answering my question, but in fact I came up with a pretty decent solution for this, based on some input from others. I will attach a sample code if someone wants to do something like this in the future. I know that there are probably better ways to do this, but here is what I did.

Basically, instead of using console.writeline from each thread, I created a new routine (TextOut) that has some logic. I also updated my input stream to read each char into a common line instead of using console.readline (). Here is the code:

Private command As String = "" Private Sub ConsoleInput() Dim cki As ConsoleKeyInfo cki = Console.ReadKey() If cki.Key = ConsoleKey.Escape Then command = "" 'Clear command ElseIf cki.Key = ConsoleKey.Backspace Then If Len(command) > 0 Then 'Make sure you don't go out of bounds For i As Integer = 0 To Len(command) Console.Write(" ") 'Clear output since new string will be shorter Next command = Left(command, Len(command) - 1) 'Shorten command by 1 char End If Console.CursorLeft = 0 'Set the cursor to the beginning of the line Console.Write(command) 'Write the command to the screen ElseIf cki.Key = ConsoleKey.Enter Then 'Command has been submitted, start checking Console.CursorLeft = 0 'Set the cursor to the beginning of the line For i As Integer = 0 To Len(command) Console.Write(" ") 'Clear output from command (hides the executed command) Next Dim tempCMD As String = command command = "" 'If/then statements for validating command goes here command = "" 'Clear command to allow new input Else command += cki.KeyChar 'Add char to command string Console.CursorLeft = 0 'Set the cursor to the beginning of the line Console.Write(command) 'Write the command to the screen End If ConsoleInput() 'Loop for more input End Sub Sub TextOut(ByVal message As String) If command <> "" Then For i As Integer = 0 To Len(command) Console.Write(" ") 'Clears output in case output is shorter than current command Next Console.CursorLeft = 0 'Sets cursor to beginning of row End If Console.WriteLine(message) 'Writes the current message to the screen If message <> command Then Console.Write(command) 'Writes the command back to the screen End If End Sub 
0
source

A few suggestions:

  • Write messages in the queue, instead of writing directly to the console. When the user enters a command, pulls all the latest messages from the queue and dump them to the console.
  • Messages to another area of ​​the console, like the old BBS chat user interfaces. There is one section page for messages and another for input and user command results. This requires a bit more work on the console side.
  • Output messages to a separate log file with which users can follow tail or something similar, or an application, and the user can immediately open two applications, one to display messages and other interactive commands and see the output.
+1
source

C Sharp equivalent

 using System; using System.IO; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Timers; namespace testconsole { class Program { private static bool Running = true; private static string Command = ""; static void Main( string[] args ) { while ( Running ) { ReadInput(); }//running }//main----------------------------------------- static void ReadInput() { ConsoleKeyInfo cki; cki = Console.ReadKey(); if ( cki.Key == ConsoleKey.Escape ) { Command = ""; } else if (cki.Key == ConsoleKey.Backspace) { ClearConsole(); if ( Command.Length > 0 ) { Command = Command.Substring( 0, Command.Length - 1 ); } Console.CursorLeft = 0; Console.Write( Command ); } else if ( cki.Key == ConsoleKey.Enter ) { Console.CursorLeft = 0; ClearConsole(); string TempCommand = Command; TextOut( Command ); Command = ""; //process tempcommand if ( TempCommand == "quit") { Running = false; } if ( TempCommand == "test" ) { TextOut("this is a test"); } } else { Command += cki.KeyChar; Console.CursorLeft = 0; Console.Write( Command ); } }//ReadInput----------------------------------------- static void ClearConsole() { for ( int i = 0; i < Command.Length; i++ ) { Console.Write( " " ); } }//ClearConsole----------------------------------------- static void TextOut( string Message ) { if (Command != "") { ClearConsole(); } Console.CursorLeft = 0; Console.WriteLine( Message ); if ( Message != Command ) { Console.Write( Command ); } }//TextOut----------------------------------------- }//program }//namespace 
+1
source

Here's a slightly different version that supports read prefixes and packs its own class into it for reuse. It also uses StringBuilder instead of strings for readline.

 public static class Console2 { private static bool _isReading; private static string _currentPrefix; private static readonly StringBuilder CurrentReadLine = new StringBuilder(); public static string ReadLine(string prefix = null) { _currentPrefix = prefix; _isReading = true; Console.Write(prefix); while (true) { ConsoleKeyInfo cki; cki = Console.ReadKey(); if (cki.Key == ConsoleKey.Escape) { CurrentReadLine.Clear(); } else if (cki.Key == ConsoleKey.Backspace) { if (CurrentReadLine.Length > 0) { CurrentReadLine.Length--; } ClearConsole(); Console.Write(prefix + CurrentReadLine); } else if (cki.Key == ConsoleKey.Enter) { Console.WriteLine(); var result = CurrentReadLine.ToString(); CurrentReadLine.Clear(); _isReading = false; return result; } else { CurrentReadLine.Append(cki.KeyChar); } } } static void ClearConsole() { var length = Console.CursorLeft; Console.CursorLeft = 0; for (int i = 0; i <= length; i++) { Console.Write(" "); } Console.CursorLeft = 0; } public static void WriteLine(string format, params object[] args) { ClearConsole(); Console.WriteLine(format, args); if (_isReading) { Console.Write(_currentPrefix + CurrentReadLine); } } } 
+1
source

Use a centralized registrar and disable output when user input.

0
source

Source: https://habr.com/ru/post/1432710/


All Articles