mirror of
https://github.com/binlaab/nanofiles.git
synced 2026-07-01 18:06:29 +02:00
315 lines
10 KiB
Java
315 lines
10 KiB
Java
package es.um.redes.nanoFiles.logic;
|
|
|
|
import java.net.InetSocketAddress;
|
|
import java.net.ServerSocket;
|
|
import java.util.HashMap;
|
|
import java.util.LinkedList;
|
|
import java.util.Map;
|
|
import java.io.IOException;
|
|
import java.io.RandomAccessFile;
|
|
|
|
import es.um.redes.nanoFiles.tcp.client.NFConnector;
|
|
import es.um.redes.nanoFiles.application.NanoFiles;
|
|
|
|
|
|
|
|
import es.um.redes.nanoFiles.tcp.server.NFServer;
|
|
import es.um.redes.nanoFiles.udp.client.DirectoryConnector;
|
|
import es.um.redes.nanoFiles.util.FileDigest;
|
|
import es.um.redes.nanoFiles.util.FileInfo;
|
|
import es.um.redes.nanoFiles.util.FileNameUtil;
|
|
|
|
public class NFControllerLogicP2P {
|
|
// Servidor TCP local para compartir ficheros con otros peers
|
|
private NFServer fileServer = null;
|
|
|
|
|
|
|
|
protected NFControllerLogicP2P() {
|
|
}
|
|
|
|
/**
|
|
* Método para ejecutar un servidor de ficheros en segundo plano. Debe arrancar
|
|
* el servidor en un nuevo hilo creado a tal efecto.
|
|
*
|
|
* @return Verdadero si se ha arrancado en un nuevo hilo con el servidor de
|
|
* ficheros, y está a la escucha en un puerto, falso en caso contrario.
|
|
*
|
|
*/
|
|
protected boolean startFileServer() {
|
|
boolean serverRunning = false;
|
|
/*
|
|
* Comprobar que no existe ya un objeto NFServer previamente creado, en cuyo
|
|
* caso el servidor ya está en marcha.
|
|
*/
|
|
if (fileServer != null) {
|
|
System.err.println("File server is already running");
|
|
} else {
|
|
/*
|
|
* TODO: (Boletín Servidor TCP concurrente) Arrancar servidor en segundo plano
|
|
* creando un nuevo hilo, comprobar que el servidor está escuchando en un puerto
|
|
* válido (>0), imprimir mensaje informando sobre el puerto de escucha, y
|
|
* devolver verdadero. Las excepciones que puedan lanzarse 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 sin abortar el
|
|
* programa
|
|
*
|
|
*/
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
}
|
|
return serverRunning;
|
|
|
|
}
|
|
|
|
protected void testTCPServer() {
|
|
assert (NanoFiles.testModeTCP);
|
|
/*
|
|
* Comprobar que no existe ya un objeto NFServer previamente creado, en cuyo
|
|
* caso el servidor ya está en marcha.
|
|
*/
|
|
assert (fileServer == null);
|
|
try {
|
|
|
|
fileServer = new NFServer();
|
|
/*
|
|
* (Boletín SocketsTCP) Inicialmente, se creará un NFServer y se ejecutará su
|
|
* método "test" (servidor minimalista en primer plano, que sólo puede atender a
|
|
* un cliente conectado). Posteriormente, se desactivará "testModeTCP" para
|
|
* implementar un servidor en segundo plano, que se ejecute en un hilo
|
|
* secundario para permitir que este hilo (principal) siga procesando comandos
|
|
* introducidos mediante el shell.
|
|
*/
|
|
fileServer.test();
|
|
// Este código es inalcanzable: el método 'test' nunca retorna...
|
|
} catch (IOException e1) {
|
|
e1.printStackTrace();
|
|
System.err.println("Cannot start the file server");
|
|
fileServer = null;
|
|
}
|
|
}
|
|
|
|
public void testTCPClient() {
|
|
|
|
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
|
|
* 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
|
|
* servidores.
|
|
*/
|
|
|
|
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
|
|
e.printStackTrace();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Método para listar los ficheros de un peer concreto vía TCP e imprimirlos por
|
|
* pantalla.
|
|
*
|
|
* @param La dirección del peer cuyos ficheros se quiere listar
|
|
* @return Verdadero si se ha obtenido exitosamente el listado de fichero del
|
|
* peer
|
|
*/
|
|
protected boolean listPeerFiles(InetSocketAddress peerAddr) {
|
|
boolean success = false;
|
|
|
|
try {
|
|
NFConnector nfc = new NFConnector(peerAddr);
|
|
FileInfo[] filelist = nfc.getFileList();
|
|
|
|
if (filelist == null) return success;
|
|
|
|
System.out.println("Files available in peer: ");
|
|
FileInfo.printToSysout(filelist);
|
|
success = true;
|
|
} catch (IOException e) {}
|
|
|
|
|
|
|
|
|
|
return success;
|
|
}
|
|
|
|
/**
|
|
* Descarga un fichero identificado por subcadena de hash desde uno o varios
|
|
* peers. Si se pasa "*" como nickname, usa el directorio para localizar los
|
|
* peers que tienen el hash.
|
|
*/
|
|
protected boolean downloadFromPeers(NFControllerLogicDir dirLogic, String targetPeerNickname,
|
|
String targetHashSubstring) {
|
|
// TODO: localizar peers con el hash solicitado (o uno concreto) y delegar en
|
|
// downloadFileFromServers
|
|
boolean success = false;
|
|
if (targetPeerNickname.equals("*")) {
|
|
Map<String, InetSocketAddress[]> peersWithFile = new HashMap<String, InetSocketAddress[]>();
|
|
try {
|
|
DirectoryConnector dc = new DirectoryConnector(dirLogic.getDirectoryHostname()); // la verdad que debería poder sacarlo de dirLogic
|
|
peersWithFile = dc.searchFilesByHash(targetHashSubstring);
|
|
|
|
} catch (IOException e) { e.printStackTrace(); }
|
|
if (peersWithFile.isEmpty()) return false;
|
|
success = downloadFileFromServers(peersWithFile.values().iterator().next(), peersWithFile.keySet().iterator().next()); // dios
|
|
|
|
} else {
|
|
try {
|
|
if (dirLogic.fetchPeerList().get(targetPeerNickname) == null) {
|
|
System.err.println(targetPeerNickname + " is not a registered peer");
|
|
return success;
|
|
}
|
|
InetSocketAddress[] peerAddr = new InetSocketAddress[] {dirLogic.fetchPeerList().get(targetPeerNickname)};
|
|
NFConnector nfc = new NFConnector(peerAddr[0]);
|
|
FileInfo[] peerFiles = nfc.getFileList();
|
|
|
|
FileInfo[] found = FileInfo.lookupHashSubstring(peerFiles, targetHashSubstring);
|
|
if (found.length > 1) {
|
|
System.err.println("The hash substring provided is ambiguous");
|
|
return success;
|
|
} else if (found.length == 0) {
|
|
System.err.println("The hash substring provided could not be found in peer " + targetPeerNickname);
|
|
return success;
|
|
}
|
|
|
|
success = downloadFileFromServers(peerAddr, found[0].fileHash); // voy a darle el hash entero, más fácil que volver a buscarlo
|
|
} catch (IOException e) { e.printStackTrace(); }
|
|
}
|
|
System.out.println("Hemos descargado algo? success = " + success);
|
|
return success;
|
|
}
|
|
/**
|
|
* Método para descargar un fichero del peer servidor de ficheros
|
|
*
|
|
* @param serverAddressList La lista de direcciones de los servidores a los
|
|
* que se conectará
|
|
* @param targetHashSubstring Subcadena del hash del fichero a descargar
|
|
*/
|
|
protected boolean downloadFileFromServers(InetSocketAddress[] serverAddressList, String targetHash) {
|
|
boolean downloaded = false;
|
|
|
|
if (serverAddressList.length == 0) {
|
|
System.err.println("* Cannot start download - No list of server addresses provided");
|
|
return false;
|
|
}
|
|
// TODO: crear conectores TCP solo a los servidores que confirmen el hash
|
|
// pedido, obtener nombre remoto, reservar nombre local sin colisiones, alternar
|
|
// descarga de chunks y verificar hash final. Cerrar los sockets al terminar.
|
|
|
|
|
|
long filesize = -1;
|
|
String filename = "";
|
|
|
|
NFConnector[] peerConns = new NFConnector[serverAddressList.length];
|
|
for (int i = 0; i < serverAddressList.length; i++) {
|
|
try {
|
|
NFConnector nfc = new NFConnector(serverAddressList[i]);
|
|
if (filesize == -1) {
|
|
FileInfo[] files = nfc.getFileList();
|
|
FileInfo fileToDownload = FileInfo.lookupHashSubstring(files, targetHash)[0]; // espero solo un archivo
|
|
filesize = fileToDownload.fileSize;
|
|
filename = toDisplayPath(FileNameUtil.chooseAvailableName(NanoFiles.sharedDirname + "/" + fileToDownload.fileName));
|
|
}
|
|
peerConns[i] = nfc;
|
|
} catch (IOException e) { e.printStackTrace(); }
|
|
}
|
|
|
|
|
|
try (RandomAccessFile raf = new RandomAccessFile(filename, "rw")) {
|
|
raf.setLength(filesize);
|
|
int chunks = (int) Math.ceil((double) filesize / NFConnector.CHUNK_SIZE);
|
|
|
|
for (int i = 0; i < chunks; i++) {
|
|
NFConnector nfc = peerConns[i % peerConns.length];
|
|
if (!nfc.downloadChunk(targetHash, i, raf)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
downloaded = true;
|
|
} catch (IOException e) { e.printStackTrace(); }
|
|
finally {
|
|
for (NFConnector nfc : peerConns) {
|
|
if (nfc != null) {
|
|
nfc.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
downloaded = targetHash.equals(FileDigest.computeFileChecksumString(filename));
|
|
|
|
return downloaded;
|
|
}
|
|
|
|
private String toDisplayPath(java.nio.file.Path path) {
|
|
java.nio.file.Path abs = path.toAbsolutePath().normalize();
|
|
java.nio.file.Path cwd = java.nio.file.Paths.get("").toAbsolutePath().normalize();
|
|
if (abs.startsWith(cwd)) {
|
|
return cwd.relativize(abs).toString();
|
|
}
|
|
return path.toString();
|
|
}
|
|
|
|
/**
|
|
* Método para obtener el puerto de escucha de nuestro servidor de ficheros
|
|
*
|
|
* @return El puerto en el que escucha el servidor, o 0 en caso de error.
|
|
*/
|
|
protected int getServerPort() {
|
|
// int port = 0;
|
|
|
|
/*
|
|
* TODO: Devolver el puerto de escucha de nuestro servidor de ficheros
|
|
*/
|
|
if (fileServer != null) {
|
|
return fileServer.getServerSocket().getLocalPort();
|
|
} else return 0;
|
|
}
|
|
|
|
/**
|
|
* Método para detener nuestro servidor de ficheros en segundo plano
|
|
*
|
|
*/
|
|
protected void stopFileServer() {
|
|
/*
|
|
* TODO: Enviar señal para detener nuestro servidor de ficheros en segundo plano
|
|
*/
|
|
if (fileServer != null) {
|
|
fileServer.stopServer();
|
|
fileServer = null;
|
|
}
|
|
}
|
|
|
|
protected boolean serving() {
|
|
boolean result = false;
|
|
|
|
if (fileServer != null) result = true;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
}
|