<div class="page">
<div class="cover text-center">
<img class="mx-auto" src=/itb/images/logo_mislata.png alt="logo">
# Servidor multiclient
<div class="text-end fit-content ms-auto my-3 mt-auto pt-3">
<p><strong>Autor:</strong> Joan Puigcerver Ibáñez</p>
<p><strong>Correu electrònic:</strong> j.puigcerveribanez@edu.gva.es</p>
<p><strong>Curs:</strong> 2024/2025</p>
</div>
<div>
<p class="fw-bold mb-0">Llicència: BY-NC-SA</p>
<p class="d-none d-md-block">(Reconeixement - No Comercial - Compartir Igual)</p>
<a href="https://creativecommons.org/licenses/by-nc-sa/4.0/deed.ca" target="_blank">
<img class="mx-auto" src="/itb/images/license.png" alt="Licence"/>
</a>
</div><!--license-->
</div><!--cover-->
</div><!--page-->
{:toc}
## Objectius
En aquest exemple es mostrarà com es poden crear un servidor capaç de gestionar diferents
clients de manera simultània.
El codi està disponible també a GitHub: https://github.com/fpmislata-dam2s-psp/PuigcerverJoan-PSP-solucions/tree/main/src/ud3/examples
## Client: MultiClient
### Codi font
```java
package ud3.examples.multiclient.client;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ConnectException;
import java.net.Socket;
import java.util.Scanner;
public class MultiClient {
public static void main(String[] args) {
try {
String host = "localhost";
int port = 1234;
System.out.println("Creant el Socket client.");
Socket socket = new Socket(host, port);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// Es pot utilitzar l'opció autoflush
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
Scanner scanner = new Scanner(System.in);
// Identifiquem el client en el servidor.
// El servidor el primer que fa és esperar el nom
System.out.print("Introdueix el teu nom: ");
String nom = scanner.nextLine();
out.println(nom);
// El client pot enviar missatges fins que escriga END
String line;
System.out.print("Text: ");
while(!(line = scanner.nextLine()).equals("END")){
out.println(line);
System.out.print("Text: ");
}
// Tanquem la connexió al acabar
socket.close();
} catch (ConnectException e) {
System.err.println("Connection refused!");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
```
### Explicació
Aquest programa crea un client que es connecta al servidor. Després, demana un nom per identificar-se
i permeteix escriure missatges al servidor fins que s'escriga la paraula END.
Per poder executar a la vegada varies instàncies de `MultiClient` en __IntelliJ__, cal que modifiqueu la
configuració d'execució.
Heu d'anar a: __Run > Edit Configurations... > Sel·lecionar MultiClient > Build and run > Modify options > Marcar "Allow multiple instances"__
## Servidor: StartMulticlientServer
### Codi font
```java
package ud3.examples.multiclient.server;
import java.io.IOException;
import java.util.Scanner;
public class StartMulticlientServer {
public static void main(String[] args) {
try {
Scanner scanner = new Scanner(System.in);
MulticlientServer server = new MulticlientServer(1234);
server.start();
scanner.nextLine();
server.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
```
### Explicació
Aquest programa crea una instància del servidor `MulticlientServer` i l'inicia.
## Servidor: MulticlientServer
### Codi font
```java
package ud3.examples.multiclient.server;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class MulticlientServer extends Thread {
private final ServerSocket server;
private final List<ServerHandler> clients;
private boolean running;
/**
* Crea un servidor ServerSocket a partir del port.
*
* @param port Port on escoltarà el servidor
* @throws IOException Excepcions del constructor ServerSocket
*/
public MulticlientServer(int port) throws IOException {
server = new ServerSocket(port);
clients = new ArrayList<>();
running = true;
}
/**
* Para l'execució del servidor i totes les gestions dels clients
*/
public void close(){
running = false;
for (ServerHandler client : clients) {
try {
client.close();
} catch (IOException ignored) {
}
client.interrupt();
}
this.interrupt();
}
/**
* Fil d'execució del servidor.
* <p>
* El servidor escolta el port i espera noves connexions.
* Quan una nou client es connecta, es crea un objecte ServerHandler,
* que gestionarà la comunicació amb aquest client en un fil distint.
* <p>
* D'aquesta manera, el servidor pot continuar escoltant i esperant
* noves connexions mentres cada fil gestiona la comunicació
* amb cada client.
*/
@Override
public void run() {
while (running){
try {
Socket client = server.accept();
System.out.println("Nou client acceptat.");
ServerHandler handler = new ServerHandler(client);
clients.add(handler);
handler.start();
} catch (IOException e) {
System.err.println("Error while accepting new connection");
System.err.println(e.getMessage());
}
}
}
}
```
### Explicació
Aquesta classe implementa un servidor capaç de gestionar multiples clients. Aquesta classe també està
pensada per ser executada en un fil independent.
El servidor s'inicia i espera connexions. Quan una connexió es realitzada per part d'un client,
el servidor crea i inicia una instància de la classe `ServerHandler`, que s'encarrega de gestionar
les repostes amb el client.
## Servidor: ServerHandler
### Codi font
```java
package ud3.examples.multiclient.server;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
/**
* Classe que gestiona la comunicació del servidor
* amb un únic client en un fil d'execució independent.
*/
public class ServerHandler extends Thread {
private final Socket socket;
private final BufferedReader in;
private final PrintWriter out;
private String nom;
public Server(Socket socket) throws IOException {
this.socket = socket;
in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
out = new PrintWriter(socket.getOutputStream(), true);
nom = null;
}
public String getNom() {
return nom;
}
public void setNom(String nom) {
this.nom = nom;
}
public void close() throws IOException {
socket.close();
}
/**
* Fil d'execució independent per cada client.
* <p>
* Abans que res, el client s'identifica amb un nom.
* Després, el servidor mostra els missatges que cada client ha enviat.
*/
@Override
public void run() {
try {
setNom(in.readLine());
System.out.printf("%s s'ha identificat.\n", getNom());
// Quan un client es desconecta, l'operació readLine() retorna null
String message;
while((message = in.readLine()) != null){
System.out.printf("%s: %s\n", getNom(), message);
}
System.out.printf("%s has disconnected.\n", getNom());
close();
} catch (IOException e) {
System.err.println("Error while handling client.");
System.err.println(e.getMessage());
}
}
}
```
### Explicació
Aquesta classe implementa les respostes del servidor a un únic client
en un fil independent.
Aquest fil li demana el nom per identificar el client i després mostrarà tots els missatges que
el client envie al servidor fins que aquest acabe la connexió.
Aquest lloc web utilitza galetes per millorar l'experiència de l'usuari