Две проблемы:
- Ваш
while (true)
l oop блокирует поток отправки событий AWT. - Вы добавляете прослушиватель на каждой итерации вашего l oop.
Задержка очереди событий
AWT / Swing однопоточный . Когда вызывается ActionListener вашей кнопки «CONNECT TO SERVER», он вызывается в потоке отправки событий AWT. Никакие другие события не будут обрабатываться до тех пор, пока этот метод не вернется.
Поэтому, когда этот ActionListener вызывает getValue()
, а getValue () читает из сокета до тех пор, пока не будет обнаружено "stop"
, вся обработка событий приостанавливается. Ничего не перекрашивается. Не будет ответа на ввод с помощью мыши или клавиатуры, потому что MouseEvents и KeyEvents не обрабатываются.
Вы должны выполнять операции ввода-вывода, такие как чтение из сокета в другом потоке. Однако методы Swing должны выполняться в потоке отправки событий AWT.
Одним из решений является использование класса SwingWorker , чьи методы publish
и process
позволяют отправлять несколько данных элементы из фонового потока в AWT даже поток отправки.
Для отправки в сокет вы можете использовать потокобезопасную BlockingQueue для хранения строк для отправки, а затем использовать al oop в другом потоке ( не поток отправки событий AWT), чтобы получить текст из этой BlockingQueue и отправить его в сокет.
Добавить слушателя только один раз
Метод addActionListener
фактически добавляет слушателя на кнопку. Все добавленных вами слушателей будут вызываться при нажатии кнопки.
Итак, если вы вызываете addActionListener
каждый раз, когда ваш l oop выполняется, вы добавите слушателя для каждого фрагмента данных, который вы читаете из сокета!
Вам следует добавить ActionListener к кнопке только один раз - обычно сразу после того, как вы создали кнопку.
Код
Итак, если мы переместим ввод-вывод в другие потоки и используем BlockingQueue для отслеживания строк для отправки, это будет выглядеть примерно так:
private final BlockingQueue<String> linesToSend =
new LinkedBlockingDeque<>();
public DonorChat() {
// ...
textField.addActionListener(new ActionListener() {
linesToSend.add(textField.getText());
});
btnNewButton.addActionListener(new ActionListener() {
linesToSend.add(textField.getText());
});
// ...
}
private void getValue(Socket snSocket) {
textArea.setText("");
SwingWorker<Void, String> socketReader =
new SwingWorker<Void, String>() {
@Override
protected Void doInBackground()
throws IOException {
// Runs in another thread.
// No AWT/Swing calls allowed here!
dis = new DataInputStream(snSocket.getInputStream());
while (true) {
String s1 = dis.readUTF();
if (s1.equals("stop")) {
publish("Client wants to stop.");
break;
}
publish("Client says: " + s1);
}
snSocket.close();
return null;
}
@Override
protected void process(List<String> linesRead) {
// Runs in event dispatch thread thread.
// AWT/Swing calls only; no I/O allowed here!
for (String line : linesRead) {
textArea.append(line + "\n");
}
}
};
socketReader.execute();
// Lines to send were added in AWT event dispatch thread
// by ActionListeners.
// We want to send them to the socket in a different thread.
Runnable linesSender = new Runnable() {
@Override
public void run() {
try {
dos = new DataOutputStream(snSocket.getOutputStream());
while (true) {
dos.writeUTF(linesToSend.take());
}
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(linesSender).start();
}
Некоторые другие важные примечания:
- Никогда не записывайте пустой
catch
блок. Исключения сообщают вам, что и где пошло не так, что является очень ценной информацией. Если вы не уверены, что поместить в блок catch
, напишите e.printStackTrace();
, чтобы увидеть полную информацию об исключении. - Научитесь использовать менеджеры компоновки. A При нулевом макете все будет хорошо на вашем компьютере, но будут проблемы на других компьютерах, где установленные шрифты отличаются, а шрифты отображаются с разными размерами. («17 точек» означает ⁄₇₂ дюйма, который отображается с большим количеством пикселей на мониторе с разрешением 120 точек на дюйм, чем на мониторе с разрешением 96 точек на дюйм.) Кроме того, нулевой макет означает, что вы не можете изменить размер окна, чтобы сделать JTextArea и JTextField больше или меньше .