Files
nanofiles/es/um/redes/nanoFiles/udp/server/NFDirectoryServer.java

265 lines
9.3 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.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.
*/
/*
* TODO: (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);
/*
* TODO: 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();
}
/*
* 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) {
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)) {
operation = DirMessageOps.OPERATION_PING_OK;
} else {
operation = 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.
*/
/*
* TODO: (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;
}
default:
System.err.println("Unexpected message operation: \"" + operation + "\"");
System.exit(-1);
}
/*
* TODO: (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
*/
DirMessage msgToSend = new DirMessage(operation);
String msgToSendStr = msgToSend.toString();
byte[] dataToSend = msgToSendStr.getBytes();
InetSocketAddress clientAddr = (InetSocketAddress) pkt.getSocketAddress();
DatagramPacket pktToClient = new DatagramPacket(dataToSend, dataToSend.length, clientAddr);
socket.send(pktToClient);
}
}