mirror of
https://github.com/binlaab/nanofiles.git
synced 2026-07-01 12:17:21 +02:00
práctica 3, falta la última parte
This commit is contained in:
347
es/um/redes/nanoFiles/udp/client/DirectoryConnector.java
Normal file
347
es/um/redes/nanoFiles/udp/client/DirectoryConnector.java
Normal file
@@ -0,0 +1,347 @@
|
||||
package es.um.redes.nanoFiles.udp.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.SocketTimeoutException;
|
||||
import java.util.Map;
|
||||
import java.util.LinkedHashMap;
|
||||
import es.um.redes.nanoFiles.tcp.client.NFConnector;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* Cliente con métodos de consulta y actualización específicos del directorio
|
||||
*/
|
||||
public class DirectoryConnector {
|
||||
/**
|
||||
* Puerto en el que atienden los servidores de directorio
|
||||
*/
|
||||
private static final int DIRECTORY_PORT = 6868;
|
||||
/**
|
||||
* Tiempo máximo en milisegundos que se esperará a recibir una respuesta por el
|
||||
* socket antes de que se deba lanzar una excepción SocketTimeoutException para
|
||||
* recuperar el control
|
||||
*/
|
||||
private static final int TIMEOUT = 1000;
|
||||
/**
|
||||
* Número de intentos máximos para obtener del directorio una respuesta a una
|
||||
* solicitud enviada. Cada vez que expira el timeout sin recibir respuesta se
|
||||
* cuenta como un intento.
|
||||
*/
|
||||
private static final int MAX_NUMBER_OF_ATTEMPTS = 5;
|
||||
|
||||
/**
|
||||
* Socket UDP usado para la comunicación con el directorio
|
||||
*/
|
||||
private DatagramSocket socket;
|
||||
/**
|
||||
* Dirección de socket del directorio (IP:puertoUDP)
|
||||
*/
|
||||
private InetSocketAddress directoryAddress;
|
||||
/**
|
||||
* Nombre/IP del host donde se ejecuta el directorio
|
||||
*/
|
||||
private String directoryHostname;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public static class DownloadedFile {
|
||||
public final String filename;
|
||||
public final long filesize;
|
||||
public final byte[] data;
|
||||
public final String filehash;
|
||||
|
||||
public DownloadedFile(String filename, long fsize, byte[] data, String filehash) {
|
||||
this.filename = filename;
|
||||
this.filesize = fsize;
|
||||
this.data = data;
|
||||
this.filehash = filehash;
|
||||
}
|
||||
}
|
||||
|
||||
public DirectoryConnector(String hostname) throws IOException {
|
||||
// Guardamos el string con el nombre/IP del host
|
||||
directoryHostname = hostname;
|
||||
/*
|
||||
* DONE: (Boletín SocketsUDP) Convertir el string 'hostname' a InetAddress y
|
||||
* guardar la dirección de socket (address:DIRECTORY_PORT) del directorio en el
|
||||
* atributo directoryAddress, para poder enviar datagramas a dicho destino.
|
||||
*/
|
||||
/*
|
||||
* DONE: (Boletín SocketsUDP) Crea el socket UDP en cualquier puerto para enviar
|
||||
* datagramas al directorio
|
||||
*/
|
||||
directoryAddress = new InetSocketAddress(InetAddress.getByName(hostname), DIRECTORY_PORT);
|
||||
socket = new DatagramSocket(6767);
|
||||
}
|
||||
|
||||
/**
|
||||
* Método para enviar y recibir datagramas al/del directorio
|
||||
*
|
||||
* @param requestData los datos a enviar al directorio (mensaje de solicitud)
|
||||
* @return los datos recibidos del directorio (mensaje de respuesta)
|
||||
*/
|
||||
private byte[] sendAndReceiveDatagrams(byte[] requestData) {
|
||||
byte responseData[] = new byte[DirMessage.PACKET_MAX_SIZE];
|
||||
byte response[] = null;
|
||||
if (directoryAddress == null) {
|
||||
System.err.println("DirectoryConnector.sendAndReceiveDatagrams: UDP server destination address is null!");
|
||||
System.err.println(
|
||||
"DirectoryConnector.sendAndReceiveDatagrams: make sure constructor initializes field \"directoryAddress\"");
|
||||
System.exit(-1);
|
||||
|
||||
}
|
||||
if (socket == null) {
|
||||
System.err.println("DirectoryConnector.sendAndReceiveDatagrams: UDP socket is null!");
|
||||
System.err.println(
|
||||
"DirectoryConnector.sendAndReceiveDatagrams: make sure constructor initializes field \"socket\"");
|
||||
System.exit(-1);
|
||||
}
|
||||
/*
|
||||
* DONE¿?: (Boletín SocketsUDP) Enviar datos en un datagrama al directorio y
|
||||
* recibir una respuesta. El array devuelto debe contener únicamente los datos
|
||||
* recibidos, *NO* el búfer de recepción al completo.
|
||||
*/
|
||||
DatagramPacket pktToServer = new DatagramPacket(requestData, requestData.length, directoryAddress);
|
||||
DatagramPacket pktFromServer = new DatagramPacket(responseData, responseData.length);
|
||||
int intentos = 0;
|
||||
boolean recibido = false;
|
||||
while (intentos < MAX_NUMBER_OF_ATTEMPTS && !recibido) {
|
||||
try {
|
||||
socket.send(pktToServer);
|
||||
socket.setSoTimeout(TIMEOUT);
|
||||
socket.receive(pktFromServer);
|
||||
recibido = true;
|
||||
} catch (IOException e) {
|
||||
intentos++;
|
||||
if (intentos == MAX_NUMBER_OF_ATTEMPTS) {
|
||||
System.err.println("DirectoryConnector.sendAndReceiveDatagrams: socket.receive()");
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String servResp = new String(responseData, 0, pktFromServer.getLength());
|
||||
response = servResp.getBytes();
|
||||
System.out.println("Hemos recibido " + servResp);
|
||||
|
||||
/*
|
||||
* done: (Boletín SocketsUDP) Una vez el envío y recepción asumiendo un canal
|
||||
* confiable (sin pérdidas) esté terminado y probado, debe implementarse un
|
||||
* mecanismo de retransmisión usando temporizador, en caso de que no se reciba
|
||||
* respuesta en el plazo de TIMEOUT. En caso de salte el timeout, se debe volver
|
||||
* a enviar el datagrama y tratar de recibir respuestas, reintentando como
|
||||
* máximo en MAX_NUMBER_OF_ATTEMPTS ocasiones.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* DONE: (Boletín SocketsUDP) Las excepciones que puedan lanzarse al
|
||||
* leer/escribir en el socket deben ser capturadas y tratadas en este método. Si
|
||||
* se produce una excepción de entrada/salida (error del que no es posible
|
||||
* recuperarse), se debe informar y terminar el programa.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* NOTA: Las excepciones deben tratarse de la más concreta a la más genérica.
|
||||
* SocketTimeoutException es más concreta que IOException.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
if (response != null && response.length == responseData.length) {
|
||||
System.err.println("Your response is as large as the datagram reception buffer!!\n"
|
||||
+ "You must extract from the buffer only the bytes that belong to the datagram!");
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Método para probar la comunicación con el directorio mediante el envío y
|
||||
* recepción de mensajes sin formatear ("en crudo")
|
||||
*
|
||||
* @return verdadero si se ha enviado un datagrama y recibido una respuesta
|
||||
*/
|
||||
public boolean testSendAndReceive() {
|
||||
/*
|
||||
* DONE: (Boletín SocketsUDP) Probar el correcto funcionamiento de
|
||||
* sendAndReceiveDatagrams. Se debe enviar un datagrama con la cadena "ping" y
|
||||
* comprobar que la respuesta recibida empieza por "pingok". En tal caso,
|
||||
* devuelve verdadero, falso si la respuesta no contiene los datos esperados.
|
||||
*/
|
||||
boolean success = false;
|
||||
|
||||
byte[] reqData = new String("ping").getBytes();
|
||||
|
||||
byte[] resp = sendAndReceiveDatagrams(reqData);
|
||||
if (resp != null) {
|
||||
String respStr = new String(resp, 0 , resp.length);
|
||||
if (respStr.startsWith("pingok")) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public String getDirectoryHostname() {
|
||||
return directoryHostname;
|
||||
}
|
||||
|
||||
/**
|
||||
* Método para "hacer ping" al directorio, comprobar que está operativo y que
|
||||
* usa un protocolo compatible. Este método no usa mensajes bien formados.
|
||||
*
|
||||
* @return Verdadero si
|
||||
*/
|
||||
public boolean pingDirectoryRaw() {
|
||||
boolean success = false;
|
||||
byte[] reqData = new String("ping&" + NanoFiles.PROTOCOL_ID).getBytes();
|
||||
|
||||
byte[] resp = sendAndReceiveDatagrams(reqData);
|
||||
if (resp != null) {
|
||||
String recv = new String(resp, 0, resp.length);
|
||||
if (recv.equals("welcome")) {
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* done: (Boletín EstructuraNanoFiles) Basándose en el código de
|
||||
* "testSendAndReceive", contactar con el directorio, enviándole nuestro
|
||||
* PROTOCOL_ID (ver clase NanoFiles). Se deben usar mensajes "en crudo" (sin un
|
||||
* formato bien definido) para la comunicación.
|
||||
*
|
||||
* PASOS: 1.Crear el mensaje a enviar (String "ping&protocolId"). 2.Crear un
|
||||
* datagrama con los bytes en que se codifica la cadena : 4.Enviar datagrama y
|
||||
* recibir una respuesta (sendAndReceiveDatagrams). : 5. Comprobar si la cadena
|
||||
* recibida en el datagrama de respuesta es "welcome", imprimir si éxito o
|
||||
* fracaso. 6.Devolver éxito/fracaso de la operación.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Método para "hacer ping" al directorio, comprobar que está operativo y que es
|
||||
* compatible.
|
||||
*
|
||||
* @return Verdadero si el directorio está operativo y es compatible
|
||||
*/
|
||||
public boolean pingDirectory() {
|
||||
boolean success = false;
|
||||
/*
|
||||
* done: (Boletín MensajesASCII) Hacer ping al directorio 1.Crear el mensaje a
|
||||
* enviar (objeto DirMessage) con atributos adecuados (operation, etc.) NOTA:
|
||||
* Usar como operaciones las constantes definidas en la clase DirMessageOps :
|
||||
* 2.Convertir el objeto DirMessage a enviar a un string (método toString)
|
||||
* 3.Crear un datagrama con los bytes en que se codifica la cadena : 4.Enviar
|
||||
* datagrama y recibir una respuesta (sendAndReceiveDatagrams). : 5.Convertir
|
||||
* respuesta recibida en un objeto DirMessage (método DirMessage.fromString)
|
||||
* 6.Extraer datos del objeto DirMessage y procesarlos 7.Devolver éxito/fracaso
|
||||
* de la operación
|
||||
*/
|
||||
|
||||
DirMessage ping = new DirMessage(DirMessageOps.OPERATION_PING);
|
||||
String pingStr = ping.toString();
|
||||
byte[] pingBytes = pingStr.getBytes();
|
||||
byte[] resp = sendAndReceiveDatagrams(pingBytes);
|
||||
String respStr = new String(resp, 0, resp.length);
|
||||
DirMessage respPing = DirMessage.fromString(respStr);
|
||||
success = (respPing.getOperation().equals(DirMessageOps.OPERATION_PING_OK));
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Método para dar de alta como servidor de ficheros en el puerto indicado.
|
||||
*
|
||||
* @param serverPort El puerto TCP en el que este peer sirve ficheros a otros
|
||||
* @return Verdadero si el directorio tiene registrado a este peer como servidor
|
||||
* y acepta la lista de ficheros, falso en caso contrario.
|
||||
*/
|
||||
public boolean registerFileServer(int serverPort) {
|
||||
boolean success = false;
|
||||
|
||||
// TODO: Ver TODOs en pingDirectory y seguir esquema similar
|
||||
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
/**
|
||||
* Método para obtener la lista de ficheros alojados en el directorio. Para cada
|
||||
* fichero se debe obtener un objeto FileInfo con nombre, tamaño y hash.
|
||||
*
|
||||
* @return Los ficheros disponibles en el directorio, o null si el directorio no
|
||||
* pudo satisfacer nuestra solicitud
|
||||
*/
|
||||
public FileInfo[] getFileList() {
|
||||
FileInfo[] filelist = new FileInfo[0];
|
||||
// TODO: Ver TODOs en pingDirectory y seguir esquema similar
|
||||
|
||||
|
||||
|
||||
return filelist;
|
||||
}
|
||||
|
||||
public Map<String, InetSocketAddress> getPeerList() {
|
||||
Map<String, InetSocketAddress> peers = new LinkedHashMap<String, InetSocketAddress>();
|
||||
|
||||
|
||||
|
||||
return peers;
|
||||
}
|
||||
|
||||
public Map<String, InetSocketAddress[]> searchFilesByHash(String hashSubstring) {
|
||||
Map<String, InetSocketAddress[]> results = new LinkedHashMap<String, InetSocketAddress[]>();
|
||||
|
||||
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public DownloadedFile downloadFileFromDirectory(String hashSubstring) {
|
||||
byte[] fileData = null;
|
||||
String filename = null;
|
||||
long filesize = -1;
|
||||
String filehash = null;
|
||||
|
||||
|
||||
|
||||
return new DownloadedFile(filename, filesize, fileData, filehash);
|
||||
}
|
||||
|
||||
/**
|
||||
* Método para darse de baja como servidor de ficheros.
|
||||
*
|
||||
* @return Verdadero si el directorio tiene registrado a este peer como servidor
|
||||
* y ha dado de baja sus ficheros.
|
||||
*/
|
||||
public boolean unregisterFileServer() {
|
||||
boolean success = false;
|
||||
|
||||
|
||||
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
160
es/um/redes/nanoFiles/udp/message/DirMessage.java
Normal file
160
es/um/redes/nanoFiles/udp/message/DirMessage.java
Normal file
@@ -0,0 +1,160 @@
|
||||
package es.um.redes.nanoFiles.udp.message;
|
||||
|
||||
import es.um.redes.nanoFiles.application.NanoFiles;
|
||||
|
||||
/**
|
||||
* Clase que modela los mensajes del protocolo de comunicación entre pares para
|
||||
* implementar el explorador de ficheros remoto (servidor de ficheros). Estos
|
||||
* mensajes son intercambiados entre las clases DirectoryServer y
|
||||
* DirectoryConnector, y se codifican como texto en formato "campo:valor".
|
||||
*
|
||||
* @author rtitos
|
||||
*
|
||||
*/
|
||||
public class DirMessage {
|
||||
public static final int PACKET_MAX_SIZE = 65507; // 65535 - 8 (UDP header) - 20 (IP header)
|
||||
|
||||
private static final char DELIMITER = ':'; // Define el delimitador
|
||||
private static final char END_LINE = '\n'; // Define el carácter de fin de línea
|
||||
|
||||
/**
|
||||
* Nombre del campo que define el tipo de mensaje (primera línea)
|
||||
*/
|
||||
private static final String FIELDNAME_OPERATION = "operation";
|
||||
/*
|
||||
* TODO: (Boletín MensajesASCII) Definir de manera simbólica los nombres de
|
||||
* todos los campos que pueden aparecer en los mensajes de este protocolo
|
||||
* (formato campo:valor)
|
||||
*/
|
||||
|
||||
private static final String FIELDNAME_PROTOCOL = "protocol";
|
||||
/**
|
||||
* Tipo del mensaje, de entre los tipos definidos en PeerMessageOps.
|
||||
*/
|
||||
private String operation = DirMessageOps.OPERATION_INVALID;
|
||||
/**
|
||||
* Identificador de protocolo usado, para comprobar compatibilidad del directorio.
|
||||
*/
|
||||
private String protocolId;
|
||||
/*
|
||||
* TODO: (Boletín MensajesASCII) Crear un atributo correspondiente a cada uno de
|
||||
* los campos de los diferentes mensajes de este protocolo.
|
||||
*/
|
||||
|
||||
public DirMessage(String op) {
|
||||
operation = op;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: (Boletín MensajesASCII) Crear diferentes constructores adecuados para
|
||||
* construir mensajes de diferentes tipos con sus correspondientes argumentos
|
||||
* (campos del mensaje)
|
||||
*/
|
||||
|
||||
public String getOperation() {
|
||||
return operation;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: (Boletín MensajesASCII) Crear métodos getter y setter para obtener los
|
||||
* valores de los atributos de un mensaje. Se aconseja incluir código que
|
||||
* compruebe que no se modifica/obtiene el valor de un campo (atributo) que no
|
||||
* esté definido para el tipo de mensaje dado por "operation".
|
||||
*/
|
||||
public void setProtocolID(String protocolIdent) {
|
||||
if (!operation.equals(DirMessageOps.OPERATION_PING)) {
|
||||
throw new RuntimeException(
|
||||
"DirMessage: setProtocolId called for message of unexpected type (" + operation + ")");
|
||||
}
|
||||
protocolId = protocolIdent;
|
||||
}
|
||||
|
||||
public String getProtocolId() {
|
||||
|
||||
|
||||
|
||||
return protocolId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Método que convierte un mensaje codificado como una cadena de caracteres, a
|
||||
* un objeto de la clase PeerMessage, en el cual los atributos correspondientes
|
||||
* han sido establecidos con el valor de los campos del mensaje.
|
||||
*
|
||||
* @param message El mensaje recibido por el socket, como cadena de caracteres
|
||||
* @return Un objeto PeerMessage que modela el mensaje recibido (tipo, valores,
|
||||
* etc.)
|
||||
*/
|
||||
public static DirMessage fromString(String message) {
|
||||
/*
|
||||
* TODO: (Boletín MensajesASCII) Usar un bucle para parsear el mensaje línea a
|
||||
* línea, extrayendo para cada línea el nombre del campo y el valor, usando el
|
||||
* delimitador DELIMITER, y guardarlo en variables locales.
|
||||
*/
|
||||
// System.out.println("DirMessage read from socket:");
|
||||
// System.out.println(message);
|
||||
String[] lines = message.split(END_LINE + "");
|
||||
// Local variables to save data during parsing
|
||||
DirMessage m = null;
|
||||
|
||||
for (String line : lines) {
|
||||
int idx = line.indexOf(DELIMITER); // Posición del delimitador
|
||||
String fieldName = line.substring(0, idx).toLowerCase(); // minúsculas
|
||||
String value = line.substring(idx + 1).trim();
|
||||
|
||||
switch (fieldName) {
|
||||
case FIELDNAME_OPERATION: {
|
||||
assert (m == null);
|
||||
m = new DirMessage(value);
|
||||
break;
|
||||
}
|
||||
|
||||
case FIELDNAME_PROTOCOL: {
|
||||
m.setProtocolID(value);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
System.err.println("PANIC: DirMessage.fromString - message with unknown field name " + fieldName);
|
||||
System.err.println("Message was:\n" + message);
|
||||
System.exit(-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Método que devuelve una cadena de caracteres con la codificación del mensaje
|
||||
* según el formato campo:valor, a partir del tipo y los valores almacenados en
|
||||
* los atributos.
|
||||
*
|
||||
* @return La cadena de caracteres con el mensaje a enviar por el socket.
|
||||
*/
|
||||
public String toString() {
|
||||
|
||||
StringBuffer sb = new StringBuffer();
|
||||
sb.append(FIELDNAME_OPERATION + DELIMITER + operation + END_LINE); // Construimos el campo
|
||||
/*
|
||||
* TODO: (Boletín MensajesASCII) En función de la operación del mensaje, crear
|
||||
* una cadena la operación y concatenar el resto de campos necesarios usando los
|
||||
* valores de los atributos del objeto.
|
||||
*/
|
||||
|
||||
switch (operation) {
|
||||
case DirMessageOps.OPERATION_PING:
|
||||
sb.append(FIELDNAME_PROTOCOL + DELIMITER + NanoFiles.PROTOCOL_ID + END_LINE);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
sb.append(END_LINE); // Marcamos el final del mensaje
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
22
es/um/redes/nanoFiles/udp/message/DirMessageOps.java
Normal file
22
es/um/redes/nanoFiles/udp/message/DirMessageOps.java
Normal file
@@ -0,0 +1,22 @@
|
||||
package es.um.redes.nanoFiles.udp.message;
|
||||
|
||||
public class DirMessageOps {
|
||||
|
||||
/*
|
||||
* TODO: (Boletín MensajesASCII) Añadir aquí todas las constantes que definen
|
||||
* los diferentes tipos de mensajes del protocolo de comunicación con el
|
||||
* directorio (valores posibles del campo "operation").
|
||||
*/
|
||||
public static final String OPERATION_INVALID = "invalid_operation";
|
||||
public static final String OPERATION_PING = "ping";
|
||||
|
||||
public static final String OPERATION_PING_OK = "pingOk";
|
||||
public static final String OPERATION_PING_BAD = "pingBad";
|
||||
// TODO: definir las operaciones del protocolo de directorio
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
264
es/um/redes/nanoFiles/udp/server/NFDirectoryServer.java
Normal file
264
es/um/redes/nanoFiles/udp/server/NFDirectoryServer.java
Normal file
@@ -0,0 +1,264 @@
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user