Sei sulla pagina 1di 11

Chat

• Si ha un server in ascolto sulla porta 4444

• Quando un client richiede la connessione, il


server risponde con:

“Connessione accettata. Nome: “

• Il client deve rispondere con lo user name

• A questo punto ha inizio la comunicazione

Chat/2
• Funzionamento del client (verso il server):
– Legge una stringa dallo standard I/O
– Invia la stringa letta al server

• Funzionamento del client (ricezione):


– Se il messaggio ricevuto dal server è "Closing
connection...“ allora il client deve chiudere la connessione
– Altrimenti, stampa il messaggio

• Chiusura della connessione (client):


– Avviene quando viene letto “Bye.” dallo standard I/O
– Il server risponde con “Closing connection...”
– Alla ricezione di questo messaggio il client deve
abbattere la connessione
Chat/3
• Funzionamento del server (nuova connessione):
– Il server risponde con il messaggio "Connessione
accettata. Nome utente: “
– Il server attende che il client risponda con il nome del
nuovo utente

• Funzionamento del server (ricezione):


– Un messaggio ricevuto da un client è spedito in broadcast
a tutti i client connessi
– Formato del messaggio trasmesso:
• Verso il client da cui proviene l’originale (eco): il messaggio è
uguale a quello ricevuto
• Verso gli altri client: > Da <Nome>: <Messaggio>

Chat/4
• Funzionamento del server (abbattimento
di connessione):
– Avviene quando il messaggio ricevuto è “Bye.”
– Il server:
1. Risponde in broadcast con "leaving chat....“ (stesse
modalità di prima)
2. Risponde (solo al client che chiude la connessione)
con "Closing connection...“

– Dopo aver fatto quanto sopra, il server chiude


la propria estremità della connessione
Architettura del sistema
Client 1

Dati connessioni
Client 2

Server

Client n-1

Client n

Architettura del server


• Quando arriva una richiesta crea un nuovo
thread per servirla

• Il nuovo thread si occupa di tutto


public class ChatServer {
.........
ServerSocket listenSocket = new ServerSocket(4444);
while (true) {
Socket clientSocket = listenSocket.accept();
/* Avvia_nuovo_thread(clientSocket) */
}
Architettura del server/2
1. Conn. Req. Nuovo Client

Server
3. Dialogo

2. Crea nuovo
Nuovo
ClientHandler ClientHandler
public class ChatServer {
........./* chatServer è un oggetto ChatServer */
ServerSocket listenSocket = new ServerSocket(4444);
while (true) {
Socket clientSocket = listenSocket.accept();
new Thread((new ClientHandler(clientSocket, .................))).start();
}
Runnable

ClientHandler
• Ogni oggetto ClientHandler gestisce la
connessione con un client diverso (per questo la
classe è Runnable)

• Il generico oggetto ClientHandler:


1. Ricevuto il socket verso il client, crea gli stream
relativi

2. Finché non riceve “Bye.” dal client: riceve i messaggi del


client e ne fa il broadcast verso gli altri client

3. Quando riceve il messaggio “Bye.” si occupa di


abbattere la connessione (chiudere stream, socket,
ecc.)
ClientHandler/2
public class ClientHandler implements Runnable {
........................ /* riceve connSocket come parametro */
/* Inizializzazioni*/
.........................................
in = new BufferedReader(new InputStreamReader(connSocket.getInputStream()));
out = new PrintWriter(connSocket.getOutputStream(), true);
out.println("Connessione accettata. Nome utente: ");
userName = in.readLine(); 1
/* Registra il nuovo client presso il server*/
outMessage = in.readLine();
run()
while (!(outMessage.equals("Bye."))) {
messageBroadcast(outMessage); /* Invia a tutti gli altri client */
outMessage = in.readLine();
} /* End while – è stato ricevuto “Bye.” */
messageBroadcast("leaving chat....");
out.println("Closing connection...");
/* Rimuovi il client dall’elenco di quelli registrati presso il server*/
...............
}

ClientHandler/3
public class ClientHandler implements Runnable {
/* Variabili istanza*/
public ClientHandler(Socket clientSocket, ........) {
connSocket = clientSocket;
.....................
}
public void run() { 1
................
}
public void messageBroadcast(String outMessage) { 2
int numClient;
Connection nextConn = null;
run()
numClient = /* Determina no. Client registrati */
for (int i = 0; i < numClient; i++) {
if (i-esimo client == client servito da this)
out[i].println(outMessage); /* Echo */
else
out[i].println("> Da " + clientConn.userName + ": " +
outMessage);
}
}
......................
}
Problemi da risolvere - Server
1. Come gestire le connessioni
– Quali informazioni servono

– Dove sono memorizzate

2. Quali parametri la classe ChatServer passa al


costruttore di ClientHandler
– Dipende dalle scelte fatte al punto 1

3. Implementazione di ChatServer

4. Implementazione di ClientHandler

Gestione delle connessioni


class Connection {
• Si definisce una classe
Socket connSocket;
PrintWriter out;
Connection, che
BufferedReader in; rappresenta la
String userName; connessione con il
generico client
public Connection(Socket
connSocket, PrintWriter out, • L’oggetto ChatServer
BufferedReader in, String
userName) {
mantiene un vettore
this.connSocket = privato connVector, che
connSocket; implementa la lista delle
this.out = out;
connessioni attive
this.in = in;
this.userName = userName; • Gli oggetti ClientHandler
} accedono a connVector
}
attraverso metodi
public class ChatServer {
pubblici di ChatServer
private static Vector connVector; Nota: sono possibili altre
.........................................
scelte implementative
Gestione delle connessioni/2
public class ChatServer {
• Servono metodi per:
private static Vector connVector;
............................. – Aggiungere una nuova
connessione
public void addConn(Connection
newConn) { – Eliminare una
connVector.addElement(newConn); connessione
}
3 – Dato i, accedere all’i-
public void remConn(Connection
deadConn) { esima connessione
connVector.remove(deadConn);
– Conoscere il no. di
}
public Connection getConn(int i) {
connessioni attive
return (Connection) • La connessione è
connVector.elementAt(i);
}
abbattuta dal
public int getConnNumber() { ClientHandler
return connVector.size();
}
.........................

Riassunto delle classi -


Server

ChatServer

ClientHandler

Connection
ChatServer – Impl.
public class ChatServer {
private static Vector connVector;

public ChatServer() {
connVector = new Vector();
}
3
/* Metodi per accedere e modificare connVector */
public static void main(String args[]) {

System.out.println("Server is coming up.....");


ChatServer chatServer = new ChatServer();
try {
ServerSocket listenSocket = new ServerSocket(4444);
while (true) {
Socket clientSocket = listenSocket.accept();
new Thread((new ClientHandler(clientSocket,
chatServer))).start();
}
}
catch(IOException e) {}
}
}

ClientHandler – Impl.
public class ClientHandler implements Runnable {
Connection clientConn;
ChatServer server;

public ClientHandler(Socket clientSocket, ChatServer server) {


clientConn = new Connection(null, null, null, null);
clientConn.connSocket = clientSocket;
this.server = server;
}

public void messageBroadcast(String


4 outMessage) {

/* Vedi */
}
5
public void run() {

/* Vedi */
}
}
ClientHandler – Impl./2
public void run() {
String outMessage;
try {
clientConn.in = new BufferedReader(new
InputStreamReader(clientConn.connSocket.getInputStream()));
clientConn.out = new PrintWriter(clientConn.connSocket.getOutputStream(),
true);
clientConn.out.println("Connessione accettata. Nome utente: ");
clientConn.userName = clientConn.in.readLine();
server.addConn(clientConn); 5
outMessage = clientConn.in.readLine();
while (!(outMessage.equals("Bye."))) {
messageBroadcast(outMessage);
outMessage = clientConn.in.readLine();
}
messageBroadcast("leaving chat....");
clientConn.out.println("Closing connection...");
clientConn.in.close();
clientConn.out.close();
clientConn.connSocket.close();
server.remConn(clientConn);
}
catch (IOException e) {}
}

ClientHandler – Impl./3
public void messageBroadcast(String outMessage) {
int numClient;
Connection nextConn = null;

numClient = server.getConnNumber();
for (int i = 0; i < numClient; i++) {
nextConn = server.getConn(i); 4
if (clientConn.equals(nextConn))
clientConn.out.println(outMessage);
else
nextConn.out.println("> Da " + clientConn.userName + ": "
+ outMessage);
}
}
ChatClient
• Il client deve:
1. Leggere dallo standard Input

2. Scrivere quanto letto sul socket

3. Contemporaneamente: il client deve ricevere i messaggi


che arrivano dal ClientHandler e stamparli su standard
Output

• Servono due thread per il client:


– Il primo legge da standard Input e scrive sul socket

– Il secondo riceve e scrive su standard Output

ChatClient/2
public class ChatClient {

public static void main(String[] args) throws IOException {

............................

chatSocket = new Socket("localhost", 4444);

out = new PrintWriter(chatSocket.getOutputStream(), true);

in = new BufferedReader(new

InputStreamReader(chatSocket.getInputStream()));

BufferedReader stdIn = new BufferedReader( new


InputStreamReader(System.in)); 6
/* Registrazione */

/* Crea un thread in ascolto */

while (!(userInput = stdIn.readLine()).equals("Bye."))

out.println(userInput);
7
out.println(userInput);

/* Chiusura */

}
ChatClient/3
public static void main(String[] args) throws IOException {
........................... Runnable

Thread recThread = new Thread(new myReceiver(chatSocket, in)); 6


recThread.start();
while (!(userInput = stdIn.readLine()).equals("Bye."))
out.println(userInput);
out.println(userInput);
try {
recThread.join();
}
7
catch (InterruptedException e) {}
in.close();
out.close();
chatSocket.close();
stdIn.close();
}

ChatClient/4
class myReceiver implements Runnable {
BufferedReader in;
Socket chatSocket;
public myReceiver(Socket chatSocket, BufferedReader in) {
this.chatSocket = chatSocket;
this.in = in;
}
public void run() {
String fromOther = null;
try {
fromOther = in.readLine();
while(!(fromOther.equals("Closing connection..."))) {
System.out.println(fromOther);
fromOther = in.readLine();
}
System.out.println(fromOther);
}
catch(IOException e) {}
}
}