mirror of
https://github.com/binlaab/nanofiles.git
synced 2026-07-01 16:37:21 +02:00
300 lines
10 KiB
Java
300 lines
10 KiB
Java
package es.um.redes.nanoFiles.udp.server;
|
|
|
|
import java.io.File;
|
|
import java.io.IOException;
|
|
import java.net.DatagramPacket;
|
|
import java.net.DatagramSocket;
|
|
import java.net.InetSocketAddress;
|
|
import java.net.SocketException;
|
|
import java.util.Arrays;
|
|
import java.util.LinkedHashMap;
|
|
|
|
import es.um.redes.nanoFiles.application.Directory;
|
|
import es.um.redes.nanoFiles.application.NanoFiles;
|
|
import es.um.redes.nanoFiles.udp.message.DirMessage;
|
|
import es.um.redes.nanoFiles.udp.message.DirMessageOps;
|
|
import es.um.redes.nanoFiles.util.FileInfo;
|
|
import es.um.redes.nanoFiles.util.NickGenerator;
|
|
|
|
public class NFDirectoryServer {
|
|
/**
|
|
* Número de puerto UDP en el que escucha el directorio
|
|
*/
|
|
public static final int DIRECTORY_PORT = 6868;
|
|
|
|
/**
|
|
* Socket de comunicación UDP con el cliente UDP (DirectoryConnector)
|
|
*/
|
|
private DatagramSocket socket = null;
|
|
/*
|
|
* TODO: Añadir aquí como atributos las estructuras de datos que sean necesarias
|
|
* para mantener en el directorio cualquier información necesaria para la
|
|
* funcionalidad del sistema nanoFilesP2P: ficheros alojados, servidores
|
|
* registrados, etc.
|
|
*/
|
|
/**
|
|
* Lista de ficheros alojados en el directorio.
|
|
*/
|
|
private FileInfo[] directoryFiles;
|
|
/**
|
|
* Lista de servidores registrados (IP, puerto TCP).
|
|
*/
|
|
private LinkedHashMap<String, InetSocketAddress> registeredPeers;
|
|
|
|
/**
|
|
* Probabilidad de descartar un mensaje recibido en el directorio (para simular
|
|
* enlace no confiable y testear el código de retransmisión)
|
|
*/
|
|
private double messageDiscardProbability;
|
|
|
|
public NFDirectoryServer(double corruptionProbability, String directoryFilesPath) throws SocketException {
|
|
/*
|
|
* Guardar la probabilidad de pérdida de datagramas (simular enlace no
|
|
* confiable)
|
|
*/
|
|
messageDiscardProbability = corruptionProbability;
|
|
/*
|
|
* Cargar los ficheros del directorio compartido.
|
|
*/
|
|
File dir = new File(directoryFilesPath);
|
|
if (!dir.exists()) {
|
|
dir.mkdirs();
|
|
}
|
|
directoryFiles = FileInfo.loadFilesFromFolder(directoryFilesPath);
|
|
System.out.println("* Directory loaded " + directoryFiles.length + " files from " + directoryFilesPath);
|
|
/*
|
|
* DONE: (Boletín SocketsUDP) Inicializar el atributo socket: Crear un socket
|
|
* UDP ligado al puerto especificado por el argumento directoryPort en la
|
|
* máquina local,
|
|
*/
|
|
|
|
socket = new DatagramSocket(DIRECTORY_PORT);
|
|
/*
|
|
* DONE: (Boletín SocketsUDP) Inicializar atributos que mantienen el estado del
|
|
* servidor de directorio: peers registrados, etc.)
|
|
*/
|
|
|
|
registeredPeers = new LinkedHashMap<String, InetSocketAddress>();
|
|
|
|
if (NanoFiles.testModeUDP) {
|
|
if (socket == null) {
|
|
System.err.println("[testMode] NFDirectoryServer: code not yet fully functional.\n"
|
|
+ "Check that all TODOs in its constructor and 'run' methods have been correctly addressed!");
|
|
System.exit(-1);
|
|
}
|
|
}
|
|
}
|
|
|
|
public DatagramPacket receiveDatagram() throws IOException {
|
|
DatagramPacket datagramReceivedFromClient = null;
|
|
boolean datagramReceived = false;
|
|
while (!datagramReceived) {
|
|
/*
|
|
* DONE?: (Boletín SocketsUDP) Crear un búfer para recibir datagramas y un
|
|
* datagrama asociado al búfer (datagramReceivedFromClient)
|
|
*/
|
|
/*
|
|
* DONE: (Boletín SocketsUDP) Recibimos a través del socket un datagrama
|
|
*/
|
|
byte[] recvBuf = new byte[DirMessage.PACKET_MAX_SIZE];
|
|
datagramReceivedFromClient = new DatagramPacket(recvBuf, recvBuf.length);
|
|
socket.receive(datagramReceivedFromClient);
|
|
|
|
// Vemos si el mensaje debe ser ignorado (simulación de un canal no confiable)
|
|
double rand = Math.random();
|
|
if (rand < messageDiscardProbability) {
|
|
System.err.println("Directory ignored datagram from " + datagramReceivedFromClient.getSocketAddress());
|
|
} else {
|
|
datagramReceived = true;
|
|
}
|
|
|
|
}
|
|
|
|
return datagramReceivedFromClient;
|
|
}
|
|
|
|
public void runTest() throws IOException {
|
|
|
|
System.out.println("[testMode] Directory starting...");
|
|
|
|
System.out.println("[testMode] Attempting to receive 'ping' message...");
|
|
DatagramPacket rcvDatagram = receiveDatagram();
|
|
sendResponseTestMode(rcvDatagram);
|
|
|
|
System.out.println("[testMode] Attempting to receive 'ping&PROTOCOL_ID' message...");
|
|
rcvDatagram = receiveDatagram();
|
|
sendResponseTestMode(rcvDatagram);
|
|
}
|
|
|
|
private void sendResponseTestMode(DatagramPacket pkt) throws IOException {
|
|
/*
|
|
* DONE?: (Boletín SocketsUDP) Construir un String partir de los datos recibidos
|
|
* en el datagrama pkt. A continuación, imprimir por pantalla dicha cadena a
|
|
* modo de depuración.
|
|
*/
|
|
|
|
/*
|
|
* DONE: (Boletín SocketsUDP) Después, usar la cadena para comprobar que su
|
|
* valor es "ping"; en ese caso, enviar como respuesta un datagrama con la
|
|
* cadena "pingok". Si el mensaje recibido no es "ping", se informa del error y
|
|
* se envía "invalid" como respuesta.
|
|
*/
|
|
|
|
/*
|
|
* done: (Boletín Estructura-NanoFiles) Ampliar el código para que, en el caso
|
|
* de que la cadena recibida no sea exactamente "ping", comprobar si comienza
|
|
* por "ping&" (es del tipo "ping&PROTOCOL_ID", donde PROTOCOL_ID será el
|
|
* identificador del protocolo diseñado por el grupo de prácticas (ver
|
|
* NanoFiles.PROTOCOL_ID). Se debe extraer el "protocol_id" de la cadena
|
|
* recibida y comprobar que su valor coincide con el de NanoFiles.PROTOCOL_ID,
|
|
* en cuyo caso se responderá con "welcome" (en otro caso, "denied").
|
|
*/
|
|
|
|
String messageFromClient = new String(pkt.getData(), 0, pkt.getLength());
|
|
String send;
|
|
if (messageFromClient.equals("ping")) {
|
|
send = "pingok";
|
|
} else {
|
|
if (messageFromClient.startsWith("ping&")) {
|
|
String protocol = messageFromClient.substring(5);
|
|
if (protocol.equals(NanoFiles.PROTOCOL_ID)) {
|
|
send = "welcome";
|
|
} else {
|
|
send = "denied";
|
|
}
|
|
} else {
|
|
send = "invalid";
|
|
}
|
|
}
|
|
|
|
InetSocketAddress sender = (InetSocketAddress) pkt.getSocketAddress();
|
|
byte[] sendData = send.getBytes();
|
|
DatagramPacket resp = new DatagramPacket(sendData, sendData.length, sender);
|
|
socket.send(resp);
|
|
|
|
|
|
}
|
|
|
|
public void run() throws IOException {
|
|
|
|
System.out.println("Directory starting...");
|
|
|
|
while (true) { // Bucle principal del servidor de directorio
|
|
DatagramPacket rcvDatagram = receiveDatagram();
|
|
|
|
sendResponse(rcvDatagram);
|
|
|
|
}
|
|
}
|
|
|
|
private void sendResponse(DatagramPacket pkt) throws IOException {
|
|
/*
|
|
* TODO?: (Boletín MensajesASCII) Construir String partir de los datos recibidos
|
|
* en el datagrama pkt. A continuación, imprimir por pantalla dicha cadena a
|
|
* modo de depuración. Después, usar la cadena para construir un objeto
|
|
* DirMessage que contenga en sus atributos los valores del mensaje. A partir de
|
|
* este objeto, se podrá obtener los valores de los campos del mensaje mediante
|
|
* métodos "getter" para procesar el mensaje y consultar/modificar el estado del
|
|
* servidor.
|
|
*/
|
|
String receivedData = new String(pkt.getData(), 0, pkt.getLength());
|
|
System.out.println("Hemos recibido: \n" + receivedData);
|
|
DirMessage receivedMsg = DirMessage.fromString(receivedData);
|
|
/*
|
|
* done: Una vez construido un objeto DirMessage con el contenido del datagrama
|
|
* recibido, obtener el tipo de operación solicitada por el mensaje y actuar en
|
|
* consecuencia, enviando uno u otro tipo de mensaje en respuesta.
|
|
*/
|
|
String operation = DirMessageOps.OPERATION_INVALID; // TODO: Cambiar!
|
|
if (receivedMsg != null) {
|
|
operation = receivedMsg.getOperation();
|
|
}
|
|
DirMessage msgToSend = new DirMessage(DirMessageOps.OPERATION_INVALID);
|
|
/*
|
|
* TODO: (Boletín MensajesASCII) Construir un objeto DirMessage (msgToSend) con
|
|
* la respuesta a enviar al cliente, en función del tipo de mensaje recibido,
|
|
* leyendo/modificando según sea necesario el "estado" guardado en el servidor
|
|
* de directorio (atributos files, etc.). Los atributos del objeto DirMessage
|
|
* contendrán los valores adecuados para los diferentes campos del mensaje a
|
|
* enviar como respuesta (operation, etc.)
|
|
*/
|
|
switch (operation) {
|
|
// TODO: creo que hay getters y setters para esto?????
|
|
case DirMessageOps.OPERATION_PING: {
|
|
|
|
/*
|
|
* done: (Boletín MensajesASCII) Comprobamos si el protocolId del mensaje del
|
|
* cliente coincide con el nuestro.
|
|
*/
|
|
String protocolId = receivedMsg.getProtocolId();
|
|
System.out.println(protocolId.equals(NanoFiles.PROTOCOL_ID));
|
|
if (protocolId.equals(NanoFiles.PROTOCOL_ID)) {
|
|
System.out.println("Tenemos el protocolo bien");
|
|
msgToSend.setOperation(DirMessageOps.OPERATION_PING_OK);
|
|
} else {
|
|
msgToSend.setOperation(DirMessageOps.OPERATION_PING_BAD);
|
|
}
|
|
/*
|
|
* done: (Boletín MensajesASCII) Construimos un mensaje de respuesta que indique
|
|
* el éxito/fracaso del ping (compatible, incompatible), y lo devolvemos como
|
|
* resultado del método.
|
|
*/
|
|
/*
|
|
* done: (Boletín MensajesASCII) Imprimimos por pantalla el resultado de
|
|
* procesar la petición recibida (éxito o fracaso) con los datos relevantes, a
|
|
* modo de depuración en el servidor
|
|
*/
|
|
break;
|
|
}
|
|
|
|
case DirMessageOps.OPERATION_REQUEST_DIRFILES : {
|
|
msgToSend = new DirMessage(DirMessageOps.OPERATION_DIRFILES, this.directoryFiles);
|
|
break;
|
|
}
|
|
|
|
case DirMessageOps.OPERATION_SERVE: {
|
|
|
|
if (registeredPeers.put(receivedMsg.getNick(), new InetSocketAddress(receivedMsg.getIP(), receivedMsg.getPort())) == null) {
|
|
msgToSend = new DirMessage(DirMessageOps.OPERATION_SERVE_OK);
|
|
} else {
|
|
msgToSend = new DirMessage(DirMessageOps.OPERATION_SERVE_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case DirMessageOps.OPERATION_REQUEST_SERVER_PEERS: {
|
|
msgToSend = new DirMessage(DirMessageOps.OPERATION_SERVER_PEERS);
|
|
msgToSend.setPeers(registeredPeers);
|
|
break;
|
|
}
|
|
|
|
case DirMessageOps.OPERATION_STOP_SERVE: {
|
|
System.out.println("stop_serve - tenemos el nick " + receivedMsg.getNick());
|
|
if (registeredPeers.get(receivedMsg.getNick()) != null) {
|
|
registeredPeers.remove(receivedMsg.getNick());
|
|
msgToSend = new DirMessage(DirMessageOps.OPERATION_STOP_SERVE_OK);
|
|
} else {
|
|
msgToSend = new DirMessage(DirMessageOps.OPERATION_STOP_SERVE_ERROR);
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
System.err.println("Unexpected message operation: \"" + operation + "\"");
|
|
System.exit(-1);
|
|
}
|
|
|
|
/*
|
|
* done: (Boletín MensajesASCII) Convertir a String el objeto DirMessage
|
|
* (msgToSend) con el mensaje de respuesta a enviar, extraer los bytes en que se
|
|
* codifica el string y finalmente enviarlos en un datagrama
|
|
*/
|
|
String msgToSendStr = msgToSend.toString();
|
|
byte[] dataToSend = msgToSendStr.getBytes();
|
|
InetSocketAddress clientAddr = (InetSocketAddress) pkt.getSocketAddress();
|
|
DatagramPacket pktToClient = new DatagramPacket(dataToSend, dataToSend.length, clientAddr);
|
|
socket.send(pktToClient);
|
|
}
|
|
|
|
}
|