quemen redes

This commit is contained in:
binlaab
2026-04-20 22:38:38 +02:00
parent aa8b7b30fb
commit 25bf19ee9c
15 changed files with 414 additions and 42 deletions

View File

@@ -24,7 +24,7 @@ public class NanoFiles {
* Flag para pruebas iniciales con TCP, desactivado una vez que la comunicación
* cliente-servidor de ficheros está implementada y probada.
*/
public static boolean testModeTCP = true;
public static boolean testModeTCP = false;
public static String peerNickname;
public static void main(String[] args) {

View File

@@ -12,8 +12,15 @@ public class NFController {
* Diferentes estados del cliente de acuerdo con el autómata
*/
private static final byte OFFLINE = 0;
private static final byte WAIT_ACK_PING = 1;
private static final byte ONLINE = 2;
private static final byte RETRY_PING = 3;
private static final byte END = 4;
private static final byte WAIT_ACK_REQUESTDIRFILES = 5;
private static final byte RETRY_REQUESTDIRFILES = 6;
/*
* TODO: (Boletín Autómatas) Añadir más constantes que representen los estados
* DONE: (Boletín Autómatas) Añadir más constantes que representen los estados
* del autómata del cliente de directorio.
*/
@@ -138,8 +145,12 @@ public class NFController {
commandSucceeded = controllerPeer.listPeerFiles(peerAddr);
break;
case NFCommands.COM_DOWNLOAD_PEER:
commandSucceeded = controllerPeer.downloadFromPeers(controllerDir, targetPeerNickname,
if (NanoFiles.testModeTCP) {
controllerPeer.testTCPClient();
} else {
commandSucceeded = controllerPeer.downloadFromPeers(controllerDir, targetPeerNickname,
targetHashSubstring);
}
break;
case NFCommands.COM_SERVE:
/*
@@ -217,6 +228,12 @@ public class NFController {
commandAllowed = true;
break;
}
case NFCommands.COM_PING: {
// ?????
}
default:
// System.err.println("ERROR: undefined behaviour for " + currentCommand + "
// command!");

View File

@@ -1,6 +1,7 @@
package es.um.redes.nanoFiles.logic;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.io.IOException;
import es.um.redes.nanoFiles.tcp.client.NFConnector;
import es.um.redes.nanoFiles.application.NanoFiles;
@@ -46,7 +47,21 @@ public class NFControllerLogicP2P {
*
*/
try {
fileServer = new NFServer();
ServerSocket serverSocket = fileServer.getServerSocket();
if (serverSocket.isBound()) {
System.out.println("Server socket listening on port " + serverSocket.getLocalPort());
serverRunning = true;
}
Thread mainServerThread = new Thread(fileServer);
mainServerThread.start();
} catch (IOException e) {
e.printStackTrace();
System.err.println("Cannot start the file server");
fileServer = null;
}
}
@@ -86,7 +101,7 @@ public class NFControllerLogicP2P {
assert (NanoFiles.testModeTCP);
/*
* (Boletín SocketsTCP) Inicialmente, se creará un NFConnector (cliente TCP)
* para conectarse a un servidor que esté escuchando en la misma máquina y un
* para conectarse a un servidor que esté escuchando en la m isma máquina y un
* puerto fijo. Después, se ejecutará el método "test" para comprobar la
* comunicación mediante el socket TCP. Posteriormente, se desactivará
* "testModeTCP" para implementar la descarga de un fichero desde múltiples
@@ -95,6 +110,7 @@ public class NFControllerLogicP2P {
try {
NFConnector nfConnector = new NFConnector(new InetSocketAddress(NFServer.PORT));
// meter la ip como primer argumento a InetSocketAddress()
nfConnector.test();
} catch (IOException e) {
// TODO Auto-generated catch block

View File

@@ -7,6 +7,8 @@ import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.Date;
import es.um.redes.nanoFiles.tcp.message.PeerMessage;
import es.um.redes.nanoFiles.tcp.message.PeerMessageOps;
@@ -18,20 +20,25 @@ public class NFConnector {
private InetSocketAddress serverAddr;
private DataInputStream dis;
private DataOutputStream dos;
public NFConnector(InetSocketAddress fserverAddr) throws UnknownHostException, IOException {
serverAddr = fserverAddr;
/*
* TODO: (Boletín SocketsTCP) Se crea el socket a partir de la dirección del
* done: (Boletín SocketsTCP) Se crea el socket a partir de la dirección del
* servidor (IP, puerto). La creación exitosa del socket significa que la
* conexión TCP ha sido establecida.
*/
/*
* TODO: (Boletín SocketsTCP) Se crean los DataInputStream/DataOutputStream a
* done: (Boletín SocketsTCP) Se crean los DataInputStream/DataOutputStream a
* partir de los streams de entrada/salida del socket creado. Se usarán para
* enviar (dos) y recibir (dis) datos del servidor.
*/
socket = new Socket(serverAddr.getAddress(), serverAddr.getPort());
dis = new DataInputStream(socket.getInputStream());
dos = new DataOutputStream(socket.getOutputStream());
@@ -39,9 +46,33 @@ public class NFConnector {
public void test() {
/*
* TODO: (Boletín SocketsTCP) Enviar entero cualquiera a través del socket y
* done: (Boletín SocketsTCP) Enviar entero cualquiera a través del socket y
* después recibir otro entero, comprobando que se trata del mismo valor.
*/
/* double rand = Math.random() * 100;
int intNumber = (int) rand;
dos.writeInt(intNumber);
System.out.println("sent " + intNumber);
int newIntNumber = dis.readInt();
System.out.println("received " + intNumber);
*/
try {
PeerMessage msgOut = new PeerMessage(PeerMessageOps.OPCODE_REQUEST_PEER_FILES);
msgOut.writeMessageToOutputStream(dos);
System.out.println("sent " + PeerMessageOps.opcodeToOperation(msgOut.getOpcode()));
PeerMessage msgIn = PeerMessage.readMessageFromInputStream(dis);
System.out.println("received " + PeerMessageOps.opcodeToOperation(msgIn.getOpcode()));
System.out.println("(" + new SimpleDateFormat("HH:mm:ss:SS").format(new Date()));
if (msgIn.getOpcode() == PeerMessageOps.OPCODE_PEER_FILE) {
System.out.println("file content: ");
System.out.println("last = " + msgIn.getLast());
System.out.println("fileSize = " + msgIn.getFileSize());
System.out.println("hash = " + msgIn.getFileHash());
System.out.println("long = " + msgIn.getFilenameLong());
System.out.println("val = " + msgIn.getFilenameVal());
}
socket.close();
} catch (IOException e) { e.printStackTrace(); }
}

View File

@@ -10,7 +10,7 @@ public class PeerMessage {
public static final byte HASH_LENGTH = 40;
private byte opcode;
/*
@@ -18,6 +18,11 @@ public class PeerMessage {
* específicos para crear mensajes con otros campos, según sea necesario
*
*/
private boolean last;
private long fileSize;
private String fileHash;
private byte filenameLong;
private String filenameVal;
@@ -40,9 +45,49 @@ public class PeerMessage {
return opcode;
}
public boolean isLast() {
return last;
}
public void setLast(boolean last) {
this.last = last;
}
public boolean getLast() {
return last;
}
public long getFileSize() {
return fileSize;
}
public void setFileSize(long fileSize) {
this.fileSize = fileSize;
}
public String getFileHash() {
return fileHash;
}
public void setFileHash(String fileHash) {
this.fileHash = fileHash;
}
public byte getFilenameLong() {
return filenameLong;
}
public void setFilenameLong(byte filenameLong) {
this.filenameLong = filenameLong;
}
public String getFilenameVal() {
return filenameVal;
}
public void setFilenameVal(String filenameVal) {
this.filenameVal = filenameVal;
}
/**
* Método de clase para parsear los campos de un mensaje y construir el objeto
@@ -65,6 +110,31 @@ public class PeerMessage {
PeerMessage message = new PeerMessage();
byte opcode = dis.readByte();
switch (opcode) {
case PeerMessageOps.OPCODE_REQUEST_PEER_FILES:
case PeerMessageOps.OPCODE_PEER_FILES_ERROR: break;
case PeerMessageOps.OPCODE_PEER_FILE: {
boolean last = false;
byte lastVal = dis.readByte();
if (lastVal == 1) {
last = true;
}
long fileSize = dis.readLong();
byte[] fileHash = new byte[HASH_LENGTH];
dis.readFully(fileHash);
byte filenameLong = dis.readByte();
byte[] filenameVal = new byte[filenameLong];
dis.readFully(filenameVal);
message.setLast(last);
message.setFileSize(fileSize);
message.setFileHash(new String(fileHash));
message.setFilenameLong(filenameLong);
message.setFilenameVal(new String(filenameVal));
}
@@ -87,6 +157,21 @@ public class PeerMessage {
dos.writeByte(opcode);
switch (opcode) {
case PeerMessageOps.OPCODE_REQUEST_PEER_FILES:
case PeerMessageOps.OPCODE_PEER_FILES_ERROR: break;
case PeerMessageOps.OPCODE_PEER_FILE: {
byte lastVal = 0;
if (last) {
lastVal = 1;
}
dos.writeByte(lastVal);
dos.writeLong(fileSize);
dos.write(fileHash.getBytes());
dos.write(filenameLong);
dos.write(filenameVal.getBytes());
break;
}

View File

@@ -12,7 +12,9 @@ public class PeerMessageOps {
* los diferentes tipos de mensajes del protocolo de comunicación con un par
* servidor de ficheros (valores posibles del campo "operation").
*/
public static final byte OPCODE_REQUEST_PEER_FILES = 1;
public static final byte OPCODE_PEER_FILE = 2;
public static final byte OPCODE_PEER_FILES_ERROR = 3;
@@ -22,14 +24,14 @@ public class PeerMessageOps {
* su representación textual a "valid_operations_str" EN EL MISMO ORDEN.
*/
private static final Byte[] _valid_opcodes = { OPCODE_INVALID_CODE,
OPCODE_REQUEST_PEER_FILES,
OPCODE_PEER_FILE,
OPCODE_PEER_FILES_ERROR
};
private static final String[] _valid_operations_str = { "INVALID_OPCODE",
"REQUEST_PEER_FILES",
"PEER_FILE",
"PEER_FILES_ERROR"
};
private static Map<String, Byte> _operation_to_opcode;

View File

@@ -1,6 +1,9 @@
package es.um.redes.nanoFiles.tcp.server;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
@@ -11,22 +14,26 @@ public class NFServer implements Runnable {
public static final int PORT = 10000;
private NFServerState state = new NFServerState();
private ServerSocket serverSocket = null;
public ServerSocket getServerSocket() {
return serverSocket;
}
public NFServer() throws IOException {
/*
* TODO: (Boletín SocketsTCP) Crear una direción de socket a partir del puerto
* done: (Boletín SocketsTCP) Crear una direción de socket a partir del puerto
* especificado (PORT)
*/
/*
* TODO: (Boletín SocketsTCP) Crear un socket servidor y ligarlo a la dirección
* done: (Boletín SocketsTCP) Crear un socket servidor y ligarlo a la dirección
* de socket anterior
*/
InetSocketAddress serverSocketAddress = new InetSocketAddress(PORT);
serverSocket = new ServerSocket();
serverSocket.bind(serverSocketAddress);
}
/**
@@ -47,16 +54,40 @@ public class NFServer implements Runnable {
while (true) {
/*
* TODO: (Boletín SocketsTCP) Usar el socket servidor para esperar conexiones de
* done: (Boletín SocketsTCP) Usar el socket servidor para esperar conexiones de
* otros peers que soliciten descargar ficheros.
*/
/*
* TODO: (Boletín SocketsTCP) Tras aceptar la conexión con un peer cliente, la
* done: (Boletín SocketsTCP) Tras aceptar la conexión con un peer cliente, la
* comunicación con dicho cliente para servir los ficheros solicitados se debe
* implementar en el método serveFilesToClient, al cual hay que pasarle el
* socket devuelto por accept.
*/
boolean connectionOk = false;
Socket socket = null;
try {
socket = serverSocket.accept();
connectionOk = true;
} catch (IOException e) {
// conn refused
}
if (connectionOk) {
System.out.println("accepted");
try {
DataInputStream dis = new DataInputStream(socket.getInputStream());
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
int intNumber = dis.readInt();
System.out.println("received " + intNumber);
int newInt = intNumber + 1;
dos.writeInt(newInt);
System.out.println("sent " + newInt);
} catch (IOException e) {
// ???????
}
}
}
@@ -69,6 +100,7 @@ public class NFServer implements Runnable {
* @see java.lang.Runnable#run()
*/
public void run() {
boolean stopServer = false; // HAY QUE CAMBIAR ESTO PORQUE NO SÉ DE DÓNDE COJONES SALE (ver TODO l. 147)
/*
* TODO: (Boletín SocketsTCP) Usar el socket servidor para esperar conexiones de
* otros peers que soliciten descargar ficheros
@@ -87,6 +119,26 @@ public class NFServer implements Runnable {
* hilo es el que se encarga de atender al cliente conectado, no podremos tener
* más de un cliente conectado a este servidor.
*/
while (!stopServer) {
Socket socket = null;
boolean connectionOk = false;
try {
socket = serverSocket.accept();
connectionOk = true;
} catch (IOException e) {
System.err.println("Connection refused");
}
if (connectionOk) {
NFServerThread serverThread = new NFServerThread(socket, state);
serverThread.start();
int connNum = state.getNumberOfConnections();
InetSocketAddress clientAddr = (InetSocketAddress) socket.getRemoteSocketAddress();
System.out.println("New connection from " + clientAddr);
System.out.println("Total connections = " + connNum);
}
}
@@ -129,7 +181,15 @@ public class NFServer implements Runnable {
* de su hash completo.
*/
InetSocketAddress clientAddr = (InetSocketAddress) socket.getRemoteSocketAddress();
try {
while(true) {
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
DataInputStream dis = new DataInputStream(socket.getInputStream());
}
} catch (IOException e) {
e.printStackTrace();
}
}

View File

@@ -0,0 +1,29 @@
package es.um.redes.nanoFiles.tcp.server;
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
public class NFServerState {
private int numberOfConnections;
private ArrayList<Socket> sockets;
public NFServerState() {
numberOfConnections = 0;
sockets = new ArrayList<>();
}
public int getNumberOfConnections() {
return numberOfConnections;
}
public List<Socket> getSockets() {
// tu puta madre va a hacer copias
return sockets;
}
public void updateState(Socket socket) {
sockets.add(socket);
numberOfConnections++;
}
}

View File

@@ -10,7 +10,15 @@ public class NFServerThread extends Thread {
* (un socket distinto para "conversar" con un cliente)
*/
private Socket socket;
public NFServerThread(Socket externalSocket, NFServerState state) {
socket = externalSocket;
state.updateState(socket);
}
public void run() {
NFServer.serveFilesToClient(socket);
}
}

View File

@@ -276,8 +276,21 @@ public class DirectoryConnector {
boolean success = false;
// TODO: Ver TODOs en pingDirectory y seguir esquema similar
/*
* 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 serve = new DirMessage(DirMessageOps.OPERATION_SERVE);
return success;
}
@@ -291,7 +304,11 @@ public class DirectoryConnector {
*/
public FileInfo[] getFileList() {
FileInfo[] filelist = new FileInfo[0];
// TODO: Ver TODOs en pingDirectory y seguir esquema similar
DirMessage dirfiles = new DirMessage(DirMessageOps.OPERATION_DIRFILES);
byte[] dirfilesBytes = dirfiles.toString().getBytes();
byte[] resp = sendAndReceiveDatagrams(dirfilesBytes);
DirMessage respDirfiles = DirMessage.fromString(resp.toString());

View File

@@ -1,6 +1,8 @@
package es.um.redes.nanoFiles.udp.message;
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
@@ -26,6 +28,10 @@ public class DirMessage {
* todos los campos que pueden aparecer en los mensajes de este protocolo
* (formato campo:valor)
*/
private static final String FIELDNAME_FILENAME = "filename";
private static final String FIELDNAME_FILESIZE = "size";
private static final String FIELDNAME_FILEHASH = "hash";
private static final String FIELDNAME_FILEPATH = "path";
private static final String FIELDNAME_PROTOCOL = "protocol";
/**
@@ -36,6 +42,7 @@ public class DirMessage {
* Identificador de protocolo usado, para comprobar compatibilidad del directorio.
*/
private String protocolId;
private FileInfo[] fileList;
/*
* TODO: (Boletín MensajesASCII) Crear un atributo correspondiente a cada uno de
* los campos de los diferentes mensajes de este protocolo.
@@ -44,6 +51,11 @@ public class DirMessage {
public DirMessage(String op) {
operation = op;
}
public DirMessage(String op, String nick, String ip, int puerto) {
this(op);
}
/*
* TODO: (Boletín MensajesASCII) Crear diferentes constructores adecuados para
@@ -70,12 +82,22 @@ public class DirMessage {
}
public String getProtocolId() {
return protocolId;
}
public FileInfo[] getFileList () {
return fileList;
}
public void setFileList(FileInfo f) {
if (!operation.equals(DirMessageOps.OPERATION_REQUEST_DIRFILES)) {
throw new RuntimeException (
"DirMessage: setFileList called for message of type " + operation
);
}
}
/**
* 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
@@ -86,6 +108,7 @@ public class DirMessage {
* etc.)
*/
public static DirMessage fromString(String message) {
System.out.println(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
@@ -96,7 +119,13 @@ public class DirMessage {
String[] lines = message.split(END_LINE + "");
// Local variables to save data during parsing
DirMessage m = null;
String filename = null;
long filesize = 0;
String filehash = null;
String filepath = null;
// de alguna forma tengo que hacer un array de FileInfo
// ArrayList y de ahí a array?
for (String line : lines) {
int idx = line.indexOf(DELIMITER); // Posición del delimitador
String fieldName = line.substring(0, idx).toLowerCase(); // minúsculas
@@ -113,6 +142,23 @@ public class DirMessage {
m.setProtocolID(value);
break;
}
case FIELDNAME_FILENAME: {
filename = value;
break;
}
case FIELDNAME_FILESIZE: {
filesize = Long.parseLong(value);
break;
}
case FIELDNAME_FILEHASH: {
filehash = value;
FileInfo f = new FileInfo(filehash, filename, filesize, filepath);
// fileList += f;
// y esto que, muchas gracias iniesta
}
default:
@@ -150,6 +196,16 @@ public class DirMessage {
sb.append(FIELDNAME_PROTOCOL + DELIMITER + NanoFiles.PROTOCOL_ID + END_LINE);
break;
case DirMessageOps.OPERATION_DIRFILES:
FileInfo[] filelist = FileInfo.loadFilesFromFolder(Directory.DEFAULT_DIRECTORY_FILES_PATH);
sb.append(FIELDNAME_OPERATION + DELIMITER + operation + END_LINE);
for (FileInfo f : filelist) {
sb.append(FIELDNAME_FILENAME + DELIMITER + f.fileName + END_LINE);
sb.append(FIELDNAME_FILESIZE + DELIMITER + f.fileSize + END_LINE);
sb.append(FIELDNAME_FILEHASH + DELIMITER + f.fileHash + END_LINE);
sb.append(FIELDNAME_FILEPATH + DELIMITER + f.filePath + END_LINE);
}
break;
default:
break;
}

View File

@@ -12,6 +12,11 @@ public class DirMessageOps {
public static final String OPERATION_PING_OK = "pingOk";
public static final String OPERATION_PING_BAD = "pingBad";
public static final String OPERATION_REQUEST_DIRFILES = "dirfiles";
public static final String OPERATION_DIRFILES = "dirFilesList";
public static final String OPERATION_SERVE = "serve";
// TODO: definir las operaciones del protocolo de directorio

View File

@@ -9,6 +9,7 @@ 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;
@@ -140,7 +141,7 @@ public class NFDirectoryServer {
*/
/*
* TODO: (Boletín Estructura-NanoFiles) Ampliar el código para que, en el caso
* 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
@@ -200,7 +201,7 @@ public class NFDirectoryServer {
System.out.println("Hemos recibido: \n" + receivedData);
DirMessage receivedMsg = DirMessage.fromString(receivedData);
/*
* TODO: Una vez construido un objeto DirMessage con el contenido del datagrama
* 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.
*/
@@ -236,12 +237,18 @@ public class NFDirectoryServer {
* resultado del método.
*/
/*
* TODO: (Boletín MensajesASCII) Imprimimos por pantalla el resultado de
* 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 : {
FileInfo[] filelist = FileInfo.loadFilesFromFolder(Directory.DEFAULT_DIRECTORY_FILES_PATH);
operation = DirMessageOps.OPERATION_DIRFILES;
break;
}
default:
System.err.println("Unexpected message operation: \"" + operation + "\"");
@@ -249,7 +256,7 @@ public class NFDirectoryServer {
}
/*
* TODO: (Boletín MensajesASCII) Convertir a String el objeto DirMessage
* 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
*/

View File

@@ -17,7 +17,7 @@ import es.um.redes.nanoFiles.shell.NFShell;
* @author rtitos
*
* Utility class with static methods to abstract handling of file
* metadata, loading shared failes, search by name substring, etc.
* metadata, loading shared files, search by name substring, etc.
*/
public class FileInfo {
public String fileHash;