Chat client /2

Da Head First Java O'Reilly, capitolo 15. Networking e threads.

Dopo aver visto un semplice chat server multithread e un client minimale che gli si connette, vediamo ora un client che implementa anche la ricezione di messaggi dal server.

A livello di GUI il cambiamento consiste nell'introduzione di un oggetto JTextArea destinato a raccogliere i messaggi mandati dal chat server. Il metodo addIncoming() si fa carico di inizializzare la JTextArea mettendola in un pannello scrollabile e quindi nel pannello principale.

Il metodo connect() che stabilisce la connessione con il chat server estrae dal socket l'input stream, che verrà utilizzato dal chat server per mandare i messaggi, crea a partire da esso un reader su input stream, e quindi un reader bufferizzato che mette a disposizione della classe nella variabile d'istanza reader.

Ultimo cambiamento é il nuovo thread che viene creato e fatto partire al termine del costruttore del chat client. La classe che viene passata al costruttore del thread (che deve implementare l'interfaccia Runnable) é definita come inner class del chat client, MessagesReader.

Il metodo run() del MessageReader resta appeso sullo stream di input del socket, in attesa di messaggi. Come un messaggio viene ricevuto lo si aggiunge alla text area della GUI.

package Chap15;

import java.awt.BorderLayout;
import java.awt.event.*;
import java.io.*;
import java.net.Socket;
import javax.swing.*;

public class ChatClient2 {

JTextField outgoing = new JTextField(20);
JTextArea incoming = new JTextArea(15, 25);
PrintWriter writer;
BufferedReader reader;

public ChatClient2() {
JFrame frame = new JFrame("Chat Client 2");
JPanel panel = new JPanel();
JButton btnSend = new JButton("Send");
btnSend.addActionListener(new ButtonListener());

addIncoming(panel);
panel.add(outgoing);
panel.add(btnSend);
frame.getContentPane().add(BorderLayout.CENTER, panel);

frame.setSize(400, 400);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

connect();

new Thread(new MessagesReader()).start();
}

private void connect() {
try {
Socket socket = new Socket("localhost", 4242);

InputStreamReader isr = new InputStreamReader(socket.getInputStream());
reader = new BufferedReader(isr);
writer = new PrintWriter(socket.getOutputStream());
System.out.println("Connected to chat server");
}
catch (Exception ex) {
ex.printStackTrace();
}
}

private void addIncoming(JPanel panel) {
incoming.setLineWrap(true);
incoming.setWrapStyleWord(true);
incoming.setEditable(false);

JScrollPane scroller = new JScrollPane(incoming);
scroller.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scroller.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);

panel.add(scroller);
}

private class ButtonListener implements ActionListener {

public void actionPerformed(ActionEvent e) {
String txt = outgoing.getText();
System.out.println("Sending message: " + txt);
try {
writer.println(txt);
writer.flush();
}
catch (Exception ex) {
ex.printStackTrace();
}

outgoing.setText("");
outgoing.requestFocus();
}
}

private class MessagesReader implements Runnable {

public void run() {
String message;
try {
while ((message = reader.readLine()) != null) {
System.out.println("Message read: " + message);
incoming.append(message + "\n");
}
}
catch (Exception ex) {
ex.printStackTrace();
}
}
}

public static void main(String[] args) {
new ChatClient2();
}
}

Anche questo client non é molto robusto. In particolare lo si deve lanciare solo quando il server é in esecuzione.

Nel server ho preferito non implementare la classe di appoggio come interna alla classe principale del server, in quanto la interazione tra le due classi é debole. Nel caso del client, c'é una interazione più forte, il MessageReader usa due variabili di istanza del client, reader e incoming, e inoltre il suo unico metodo, run(), é compatto e non mi pare renda troppo complesso il codice della classe esterna. Mi pare perciò più semplice usare una inner class per implementare questa relazione.

Nessun commento:

Posta un commento