Salta el contingut
 

Exemple: Servidor amb múltiples clients

Autor: Joan Puigcerver Ibáñez

Correu electrònic: j.puigcerveribanez@edu.gva.es

Llicència: CC BY-NC-SA 4.0

(Reconeixement - NoComercial - CompartirIgual) 🅭

Objectius

En aquest exemple es mostrarà com es poden crear un servidor capaç de gestionar diferents clients de manera simultània.

Client: MulticlientClient

Codi font

MulticlientClient.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 MulticlientClient {
    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 permet escriure missatges al servidor fins que s'escriga la paraula END.

Per poder executar a la vegada varies instàncies de MulticlientClient en IntelliJ, cal que modifiqueu la configuració d'execució.

Heu d'anar a: Run > Edit Configurations... > Seleccionar MulticlientClient > Build and run > Modify options > Marcar "Allow multiple instances"

Servidor: MulticlientServer

Codi font

MulticlientServer.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<MulticlientServerHandler> 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 (MulticlientServerHandler 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.");
                MulticlientServerHandler handler = new MulticlientServerHandler(client);
                clients.add(handler);
                handler.start();
            } catch (IOException e) {
                System.err.println("Error while accepting new connection");
                System.err.println(e.getMessage());
            }
        }
    }

    public static void main(String[] args) {
        MulticlientServer server = null;
        try {
            server = new MulticlientServer(1234);
            server.start();
            server.join();
        } catch (IOException ex){
            System.out.println(ex.getMessage());
        } catch (InterruptedException ex){
            System.out.println("Tancant el servidor de manera segura...");
            server.close();
        }
    }

}

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 respostes amb el client.

Servidor: MulticlientServerHandler

Codi font

MulticlientServerHandler.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 MulticlientServerHandler extends Thread {
    private final Socket socket;
    private final BufferedReader in;
    private final PrintWriter out;

    private String nom;

    public MulticlientServerHandler(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ó.

Comentaris