mirror of
https://github.com/binlaab/nanofiles.git
synced 2026-07-01 16:27:22 +02:00
319 lines
8.8 KiB
Java
319 lines
8.8 KiB
Java
package es.um.redes.nanoFiles.udp.message;
|
|
|
|
import java.net.InetSocketAddress;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.Map;
|
|
|
|
import es.um.redes.nanoFiles.application.Directory;
|
|
import es.um.redes.nanoFiles.application.NanoFiles;
|
|
import es.um.redes.nanoFiles.util.FileInfo;
|
|
|
|
/**
|
|
* 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";
|
|
|
|
private static final String FIELDNAME_NICK = "nick";
|
|
private static final String FIELDNAME_IP = "ip";
|
|
private static final String FIELDNAME_PORT = "port";
|
|
|
|
private static final String FIELDNAME_FILELIST = "filelist";
|
|
|
|
private static final String FIELDNAME_PEERS = "serverPeers";
|
|
|
|
/**
|
|
* 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;
|
|
|
|
private String nick;
|
|
private String ip;
|
|
private int port;
|
|
|
|
private FileInfo[] fileList;
|
|
private Map<String, InetSocketAddress> peerList;
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
public DirMessage(String op, String nick, String ip, int puerto) {
|
|
this(op);
|
|
this.nick = nick;
|
|
this.ip = ip;
|
|
this.port = puerto;
|
|
|
|
}
|
|
|
|
public DirMessage(String op, String nick) {
|
|
this(op);
|
|
this.nick = nick;
|
|
}
|
|
|
|
public DirMessage(String op, FileInfo[] filelist) {
|
|
this(op);
|
|
this.fileList = filelist;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
public void setOperation(String op) {
|
|
this.operation = op;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
public FileInfo[] getFileList() {
|
|
return fileList;
|
|
}
|
|
|
|
public void setFileList(FileInfo[] filelist) {
|
|
if (!operation.equals(DirMessageOps.OPERATION_DIRFILES)) {
|
|
throw new RuntimeException(
|
|
"DirMessage: setFileList called for message of unexpected type (" + operation + ")");
|
|
}
|
|
fileList = filelist;
|
|
}
|
|
|
|
public String getNick() {
|
|
return nick;
|
|
}
|
|
|
|
public String getIP() {
|
|
return ip;
|
|
}
|
|
|
|
public int getPort() {
|
|
return port;
|
|
}
|
|
|
|
public void setNick(String nick) {
|
|
this.nick = nick;
|
|
}
|
|
|
|
public void setIP(String ip) {
|
|
this.ip = ip;
|
|
}
|
|
|
|
public void setPort(int port) {
|
|
this.port = port;
|
|
}
|
|
|
|
public Map<String, InetSocketAddress> getPeers() {
|
|
return this.peerList;
|
|
}
|
|
|
|
public void setPeers(Map<String, InetSocketAddress> peers) {
|
|
this.peerList = peers;
|
|
}
|
|
|
|
|
|
/**
|
|
* 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);
|
|
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;
|
|
}
|
|
|
|
case FIELDNAME_NICK: {
|
|
m.setNick(value);
|
|
break;
|
|
}
|
|
|
|
case FIELDNAME_IP: {
|
|
m.setIP(value);
|
|
break;
|
|
}
|
|
|
|
case FIELDNAME_PORT: {
|
|
m.setPort(Integer.parseInt(value));
|
|
break;
|
|
}
|
|
|
|
case FIELDNAME_FILELIST: {
|
|
String[] archivos = value.split(",");
|
|
FileInfo[] listaArchivos = new FileInfo[archivos.length];
|
|
|
|
for (int i = 0; i < archivos.length; i++) {
|
|
String[] atributos = archivos[i].split(":");
|
|
FileInfo archivo = new FileInfo(atributos[2], atributos[0], Long.parseLong(atributos[1]), atributos[3]); // de momento no meto el path
|
|
listaArchivos[i] = archivo;
|
|
}
|
|
|
|
m.setFileList(listaArchivos);
|
|
break;
|
|
}
|
|
|
|
case FIELDNAME_PEERS: {
|
|
Map<String, InetSocketAddress> peersList = new LinkedHashMap<>();
|
|
String[] peers = value.split(",");
|
|
if (peers[0].isBlank()) {
|
|
m.setPeers(peersList);
|
|
break;
|
|
}
|
|
for (String p: peers) {
|
|
System.out.println("p: " + p);
|
|
String[] partes = p.split(":");
|
|
peersList.put(partes[0], new InetSocketAddress(partes[1], Integer.parseInt(partes[2]))); }
|
|
|
|
m.setPeers(peersList);
|
|
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.
|
|
*/
|
|
|
|
// esto funciona tanto para el mensaje que entra (requestDirfiles, serve, etc.) como para el que sale (activateServeOk, dirfiles, etc.)
|
|
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;
|
|
|
|
case DirMessageOps.OPERATION_SERVE:
|
|
case DirMessageOps.OPERATION_STOP_SERVE:
|
|
sb.append(FIELDNAME_NICK + DELIMITER + this.nick + END_LINE);
|
|
sb.append(FIELDNAME_IP + DELIMITER + this.ip + END_LINE);
|
|
sb.append(FIELDNAME_PORT + DELIMITER + this.port + END_LINE);
|
|
break;
|
|
|
|
case DirMessageOps.OPERATION_DIRFILES:
|
|
sb.append(FIELDNAME_FILELIST + DELIMITER);
|
|
for (int i = 0; i < fileList.length; i++) {
|
|
FileInfo f = fileList[i];
|
|
sb.append(f.fileName + ':' + f.fileSize + ':' + f.fileHash + ':' + f.filePath);
|
|
if (i < fileList.length - 1) {
|
|
sb.append(','); // para evitar una coma al final
|
|
}
|
|
}
|
|
break;
|
|
|
|
case DirMessageOps.OPERATION_SERVER_PEERS:
|
|
sb.append(FIELDNAME_PEERS + DELIMITER);
|
|
int nPeers = 0;
|
|
for (String nick : peerList.keySet()) {
|
|
sb.append(nick + ':' + peerList.get(nick).getHostString() + ':' + peerList.get(nick).getPort());
|
|
if(nPeers++ < peerList.size()) {
|
|
sb.append(',');
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
sb.append(END_LINE); // Marcamos el final del mensaje
|
|
return sb.toString();
|
|
}
|
|
|
|
}
|