Консольный ввод-вывод Java - PullRequest
0 голосов
/ 03 мая 2019


Я написал многопоточное приложение сокетов java-сервер-клиент с функцией обмена сообщениями, но столкнулся с проблемой одновременного ввода-вывода консоли.
Главная консоль сервера прослушивает ввод с клавиатуры и одновременно распечатывает сообщения от клиентов. На стороне клиента есть отдельная нить для распечатки.
Вот упрощенное представление кода:

public class ServerThread{
....
BufferedReader in = ... (sock.getInputStream);
while(true){
  System.out.println(in.readline());
....
public class ServerMain{
.....
BufferedReader keyb = ... (System.in);
while(true){
  in = keyb.readLine();
....

Проблема возникает, когда я что-то набираю в консоли главного сервера, и в то же время приходит сообщение от одного из клиентов. Это сообщение затем заканчивается тем, что я печатал на экране, и курсор перемещается в начало следующей строки, ожидая ввода. То, что было набрано ранее, застряло в буфере клавиатуры, и я больше не могу его редактировать. Та же проблема происходит на стороне клиента.

Вопрос в том, как я могу печатать сообщения на экране, не прерывая текущий ввод?
(введенный текст также должен оставаться напечатанным на экране, как в поведении readLine () по умолчанию)

Я уже попробовал некоторые из предложенных решений для других подобных проблем:
В библиотеках Lanterna и JCurses отсутствует поддержка собственных потоков System.IO. Мне бы пришлось заново изобретать колесо и реализовать его все самостоятельно вручную из памяти на экран, по одному символу за раз плюс создание целого слоя графического интерфейса консоли.
Другая вещь использовала коды ANSI , но я не мог понять, как с ними делать то, что мне нужно. Я мог бы читать по одному входному символу за раз вместо целой строки, затем, если пришло сообщение, очистить строку, переместить курсор в начало и распечатать, но потом в следующей строке я не знаю , как печатать ранее буферизованный текст и все еще иметь возможность удалять символы с возвратом .

изменить:
GUI не является опцией , так как я хочу, чтобы мой код мог работать на сервере без монитора.
(также предположим, что на каждой стороне будет работать только один терминал, консоль, оболочка и приложение) )

Ответы [ 2 ]

1 голос
/ 03 мая 2019

Отдельный отказ от ответа, основанный на: есть только одна консоль.

И эта консоль является артефактом времен, когда несколько потоков не были настоящей проблемой. «Прекрасно работает с несколькими потоками» никогда не требовалось для этой низкоуровневой консоли.

Таким образом: если вам действительно нужно сложное решение (это не какой-то хак), просто подумайте: не используйте консоль (и) stdin / stdout.

Вместо этого: напишите простое приложение Swing GUI. Имейте одно текстовое поле ввода, где вводится, и одно или, может быть, несколько текстовых полей, куда идет ваш вывод. Если вы хотите быть модным, сделайте это веб-приложением. Я уверен, что с помощью некоторого фреймворка вы можете собрать работающую вещь за несколько часов. Делая это, вы приобретете более ценные навыки, вместо того, чтобы тратить эти часы на то, чтобы «обойти» тот факт, что вы выбрали неправильную технологию для вашей проблемы.

Обновление, учитывая комментарий ОП: тогда лучшее, что я могу придумать: не писать в консоль. Пишите в разные файлы. Откройте несколько терминалов и используйте такие инструменты, как «tail», чтобы показать вам, что происходит с вашими выходными файлами.

0 голосов
/ 04 мая 2019

Хорошо, я сам нашел идеальное решение:

JLine библиотека работает в сочетании со стандартным System.IO, также нет необходимости создавать новые объекты Terminal (вы можете) или что-либо еще. Просто вместо BufferedReader вы используете LineReader

String readLine(String prompt, Character mask, String buffer)
  • prompt (может быть нулевым) - это нерасширенный шаблон подсказки, который будет отображаться пользователю слева от строки ввода

  • mask (также может быть нулевым) - это символ, используемый для скрытия пользовательского ввода (например, при чтении пароля)

  • buffer - начальное содержимое строки ввода

Редактировать : В Документы JLine Я нашел еще лучшее решение:

printAbove

void printAbove (строка AttributedString)

Печатает строку перед приглашением и перерисовывает все. Если LineReader на самом деле не читает строку, строка будет просто распечатывается на терминал.

Параметры:
str - строка для печати *

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...