C#同时控制台输入和输出?

C#同时控制台输入和输出?,c#,console,C#,Console,我正在编写一个服务器应用程序,我希望它是基于控制台的。我需要用户能够输入不同的命令,但同时也有可能在用户编写时将某些内容输出到控制台。这会弄乱缓冲区。有什么干净的方法可以做到这一点吗 谢谢。您是否尝试过呼叫、读取任何输入并重置其位置,然后写入输出流。之后,您可以再次调用OpenStandardInput并将数据填充回流中。如果需要在用户键入时允许输出到达,我建议将输出发送到新窗口。因此,您可以有一个用于启动应用程序的窗口,然后它生成一个线程以打开新控制台进行输入,然后它继续向原始窗口发送任何输出

我正在编写一个服务器应用程序,我希望它是基于控制台的。我需要用户能够输入不同的命令,但同时也有可能在用户编写时将某些内容输出到控制台。这会弄乱缓冲区。有什么干净的方法可以做到这一点吗


谢谢。

您是否尝试过呼叫、读取任何输入并重置其位置,然后写入输出流。之后,您可以再次调用OpenStandardInput并将数据填充回流中。

如果需要在用户键入时允许输出到达,我建议将输出发送到新窗口。因此,您可以有一个用于启动应用程序的窗口,然后它生成一个线程以打开新控制台进行输入,然后它继续向原始窗口发送任何输出消息。我认为,如果您试图将所有内容都保持在同一个窗口中,您将遇到太多的资源锁定问题。

我认为,实现这一点没有完美的方法。telnet所做的(至少是我使用的上一个版本)不是打印任何输入(只是读取击键),只是在输出到达时打印输出。另一种方法是将需要输出到控制台的任何数据存储在缓冲区中,并仅在用户完成输入命令后打印。(你甚至可以给输出加上时间戳,使其更加明显。)我真的看不到更好的替代方案——使用同步I/O接口(即命令行)不可避免地会遇到问题与后端的异步操作一起。

如果将服务器视为客户机/服务器应用程序,这类问题会变得稍微简单一些。让服务器与发送命令和接收输出的客户端管理应用程序建立“n”连接。客户端应用程序可以完全分离输入和输出,一个线程处理输入,一个线程处理输出


如果输入线程位于输入行的中间,输出线程可能会阻塞,并且当该行被取消或提交时解除阻塞。

< P>我开始研究一个测试程序,以显示如何将控制台划分为输出区和输入区,当输出区域随着更多输出而扩展时,输入区域向下移动。它还不完美,但您可以将其发展为您所寻找的答案:

  static int outCol, outRow, outHeight = 10;

  static void Main(string[] args)
  {
     bool quit = false;
     System.DateTime dt = DateTime.Now;
     do
     {
        if (Console.KeyAvailable)
        {
           if (Console.ReadKey(false).Key == ConsoleKey.Escape)
              quit = true;
        }
        System.Threading.Thread.Sleep(0);
        if (DateTime.Now.Subtract(dt).TotalSeconds > .1)
        {
           dt = DateTime.Now;
           WriteOut(dt.ToString(" ss.ff"), false);
        }            
     } while (!quit);
  }

  static void WriteOut(string msg, bool appendNewLine)
  {
     int inCol, inRow;
     inCol = Console.CursorLeft;
     inRow = Console.CursorTop;

     int outLines = getMsgRowCount(outCol, msg) + (appendNewLine?1:0);
     int outBottom = outRow + outLines;
     if (outBottom > outHeight)
        outBottom = outHeight;
     if (inRow <= outBottom)
     {
        int scrollCount = outBottom - inRow + 1;
        Console.MoveBufferArea(0, inRow, Console.BufferWidth, 1, 0, inRow + scrollCount);
        inRow += scrollCount;
     }
     if (outRow + outLines > outHeight)
     {
        int scrollCount = outRow + outLines - outHeight;
        Console.MoveBufferArea(0, scrollCount, Console.BufferWidth, outHeight - scrollCount, 0, 0);
        outRow -= scrollCount;
        Console.SetCursorPosition(outCol, outRow);
     }
     Console.SetCursorPosition(outCol, outRow);
     if (appendNewLine)
        Console.WriteLine(msg);
     else
        Console.Write(msg);
     outCol = Console.CursorLeft;
     outRow = Console.CursorTop;
     Console.SetCursorPosition(inCol, inRow);
  }

  static int getMsgRowCount(int startCol, string msg)
  {
     string[] lines = msg.Split('\n');
     int result = 0;
     foreach (string line in lines)
     {
        result += (startCol + line.Length) / Console.BufferWidth;
        startCol = 0;
     }
     return result + lines.Length - 1;
  }
static int outCol,outRow,outHeight=10;
静态void Main(字符串[]参数)
{
bool-quit=false;
System.DateTime dt=DateTime.Now;
做
{
如果(控制台键可用)
{
if(Console.ReadKey(false).Key==ConsoleKey.Escape)
退出=真;
}
系统线程线程睡眠(0);
if(DateTime.Now.Subtract(dt).TotalSeconds>0.1)
{
dt=日期时间。现在;
注销(dt.ToString(“ss.ff”),虚假;
}            
}而(!退出);
}
静态空写输出(字符串msg,bool appendNewLine)
{
int inCol,inRow;
inCol=控制台。光标左键;
inRow=Console.CursorTop;
int outLines=getmsgrowtcount(outCol,msg)+(appendNewLine?1:0);
int outBottom=outRow+轮廓;
如果(外底>外高)
外底=外高;
if(进出高度)
{
int scrollCount=outRow+outLines-outHeight;
Console.MoveBufferArea(0,scrollCount,Console.BufferWidth,outHeight-scrollCount,0,0);
outRow-=滚动计数;
控制台。设置光标位置(outCol,outRow);
}
控制台。设置光标位置(outCol,outRow);
如果(添加换行符)
控制台写入线(msg);
其他的
控制台写入(msg);
outCol=Console.CursorLeft;
outRow=Console.CursorTop;
控制台。设置光标位置(inCol,inRow);
}
静态int getmsgrowtcount(int startCol,字符串msg)
{
字符串[]行=msg.Split('\n');
int结果=0;
foreach(行中的字符串行)
{
结果+=(startCol+line.Length)/Console.BufferWidth;
startCol=0;
}
返回结果+行。长度-1;
}

就我个人而言,我会使用事件处理程序来管理一个同时处理输入和输出的控制台,创建一个类ScreenManager或其他什么,在该类中添加一个void RunProgram()方法,创建一个带有处理程序和读取输入键“console.ReadKey(bool.key)”所需变量的事件

在主程序上,创建类“whatev you called it”的实例,然后创建该实例内部方法的线程,thread coreThread=new thread(delegate(){myinstance.myProgramMrthod()})

在主线程中循环,直到线程启动并运行。而(!Thread.IsAlive)

然后创建主程序循环

while (true)
{
}
然后为了安全起见,加入自定义线程,以便在关闭/释放自定义线程之前主程序不会继续

customThread.Join();
现在有两个线程分别运行

回到类,在事件处理程序方法内创建一个开关

switch (newkey)
{
case Consolekey.enter
Console.WriteLine("enter pressed");
break;

ect, ect.

default:
Console.write(newkey); // writes character key that dont match above conditions to the screen. 
break;
}
把你所有的逻辑都放在你想要如何处理钥匙上。 可能会有所帮助

在实例的方法RunProgram()或您选择调用它的方法中,在完成所有需要的代码后,创建一个无限循环来检查密钥更改

while (true)
{
newKey = Console.ReadKey(true).Key;
if (newKey != oldKey)
{
KeyChange.Invoke();
}
}
此循环存储任何按下的键,然后检查是否有新键,如果为true,则触发事件

现在,您已经拥有了所需内容的核心,一个字符串循环askng以获取新键,而主循环可以自由显示您希望显示的任何文本

我可以想到两个可以修复的错误,一个是开关内部的“默认”将以大写或字符串打印到控制台。另一个是,添加到控制台的任何文本都会添加到光标点,因此它会添加到用户刚刚输入的文本中

不管我怎么说,因为我刚刚完成了,你必须如何管理添加到控制台的文本。我再次使用一个事件。我应该去见他
while (true)
{
newKey = Console.ReadKey(true).Key;
if (newKey != oldKey)
{
KeyChange.Invoke();
}
}
public void OnInsert(string Argument)
        {
            Console.CursorTop -= 1;

    // moves the cursor to far left so new input overwrites the old.



    // if arg string is longer, then print arg string then print input  // string.

            if (Argument.Length > input.Length)
            {
                Console.WriteLine(Argument);
                Console.WriteLine(input);
            }
            else
            {

    // if the users input if longer than the argument text then print
    // out the argument text, then print white spaces to overwrite the
    // remaining input characters still displayed on screen.


                for (int i = 0; i < input.Length;i++ )
                {
                    if (i < Argument.Length)
                    {
                        Console.Write(Argument[i]);
                    }
                    else
                    {
                        Console.Write(' ');
                    }
                }
                Console.Write(Environment.NewLine);
                Console.WriteLine(input);
            }
        }

hope this helps some of you, its not perfect, a quick put together test that works enough to be built on.