diff --git a/centralised/compile.sh b/centralised/compile.sh index d512521..febe8c3 100755 --- a/centralised/compile.sh +++ b/centralised/compile.sh @@ -4,4 +4,4 @@ if [ ! -d bin ]; then mkdir bin fi -javac -d bin -sourcepath src/client/ src/client/*.java +javac -d bin -sourcepath src/client/ src/client/*.java $1 diff --git a/centralised/config b/centralised/config index e73bf6b..eeb5e65 100644 --- a/centralised/config +++ b/centralised/config @@ -21,14 +21,23 @@ socketClientTimeout = 60000 socketServerTimeout = 60000 #Port d'écoute des autres pairs -listenPort = 60000 +listenPort = 6000 #Port découte du tracker -trackerPort = 6000 +trackerPort = 5000 #Adresse IP du tracker trackerIP = 127.0.0.1 #Nombre de pieces téléchargés à la fois au maximum -maxDownloadedPieces = 4 +maxDownloadedPieces = 8 + +#Nombre maximum de connexion bers d'autres pairs +maxPeerConnections = 5 + +#Nombre de milisecondes utilisé pou rcalculer le débit +rateMeanTime = 1000 + +#Temps entre chaque mise à jour d tracker et des pairs en milisecondes +updateTime = 10000 diff --git a/centralised/downloads/toto b/centralised/downloads/toto deleted file mode 100644 index e69de29..0000000 diff --git a/centralised/run.sh b/centralised/run.sh index ea1bf2e..90bc2c1 100755 --- a/centralised/run.sh +++ b/centralised/run.sh @@ -1,4 +1,4 @@ #! /bin/bash -java -ea -cp bin App +java -ea -cp bin App $1 $2 $3 $4 diff --git a/centralised/src/client/App.java b/centralised/src/client/App.java index c0f270a..8f3ffa4 100644 --- a/centralised/src/client/App.java +++ b/centralised/src/client/App.java @@ -30,40 +30,31 @@ class App /** * Fonction main */ - public static void main(String args[]) + public static void main(String[] args) { - App.config.load("config"); + String configfile; + if (args.length == 0) { + configfile = "config"; + } + else if (args.length == 1) { + configfile = args[0]; + } + else { + System.out.println("Bad usage : [config_file]"); + return; + } + + App.config.load(configfile); App.files.init(); App.downloads.initServer(); App.downloads.initDownloads(); + CLI.init(); + //testConfig(); //testFileManager(); //testBuffermap(); //testFileShared(); - - try { - /* - Thread autoRefresher = new Thread(new Runnable(){ - @Override - public void run(){ - while (true){ - try{ - wait(((Integer)(App.config.get("timeslice"))).intValue()*1000); - // ->"have $key $BufferMap" au pairs connectes - }catch (Exception ex){ - ex.printStackTrace(); - } - } - } - }); - */ - //Protocol s = new Protocol("127.0.0.1", 6000); - //s.serverReadAndDispatch();4 - } - catch (Exception e) { - e.printStackTrace(); - } } /** diff --git a/centralised/src/client/Buffermap.java b/centralised/src/client/Buffermap.java index 0859ea3..3b9d6a8 100644 --- a/centralised/src/client/Buffermap.java +++ b/centralised/src/client/Buffermap.java @@ -2,6 +2,8 @@ * Représente et implémente un buffermap */ +import java.util.Random; + class Buffermap { /** @@ -31,8 +33,9 @@ public Buffermap(byte[] buffer, int nbpieces) for (int i=0;i 0 ? true : false; + byte mask = (byte)((0x80 & 0xff)>>> numBit); + _buffer[i] = (((0xff & mask) & (0xff & buffer[numByte])) != 0x00) + ? true : false; if (!_buffer[i]) { _missingpieces++; } @@ -97,9 +100,13 @@ public byte[] rawBuffer() byte tmp = 0x00; for (int j=0;j<8;j++) { if (i*8+j < _buffer.length) { - if (_buffer[i*8+j]) tmp += 1; + if (_buffer[i*8+j]) { + tmp += 1; + } + } + if (j < 7) { + tmp = (byte)(tmp << (byte)1); } - tmp = (byte)(tmp << (byte)1); } buffer[i] = tmp; } @@ -143,6 +150,7 @@ public void setBit(int n, boolean val) * Renvoi au maximum nb index de * pièces manquantes */ + /* public int[] getMissingPieces(int nb) { int min = Math.min(nb, _missingpieces); @@ -161,6 +169,7 @@ public int[] getMissingPieces(int nb) return indexes; } + */ /** * Renvoi au maximum nb pièces que le buffermap @@ -174,25 +183,49 @@ public int[] getDownloadPieces(Buffermap pair, int nb) int[] indexes = new int[nb]; + Random generator = new Random(); int k = 0; - for (int i=0;i<_buffer.length;i++) { - if (!_buffer[i] && pair._buffer[i]) { + int i = generator.nextInt() % _buffer.length; + if (i < 0) { + i = -i; + } + for (int j=0;j<_buffer.length;j++) { + if (!this.getBit(i) && pair.getBit(i)) { indexes[k] = i; k++; } if (k >= nb) { break; } + i++; + if (i >= _buffer.length) { + i = 0; + } } if (k < nb) { int[] tmp = new int[k]; - for (int i=0;i 0) { + System.out.println("Files ("+files.length+") :"); + } + else { + System.out.println("No file"); + } + + for (int i=0;i 0 && choice <= data.length/4) { + int i = (choice-1)*4; + String name = data[i]; + int size = Integer.parseInt(data[i+1]); + int piecesize = Integer.parseInt(data[i+2]); + String key = data[i+3]; + App.downloads.startDownload(name, key, size, piecesize); + return; + } + } + catch (NumberFormatException e) { + } + System.out.println("Invalid input : " + command); + } + } + + /** + * Affiche les statistiques + */ + private void stats() + { + FileShared[] files = App.files.getAllFiles(); + if (files.length > 0) { + System.out.println("Files ("+files.length+") :"); + } + else { + System.out.println("No file"); + } + + for (int i=0;i 0) { + System.out.println( + "Known peers (" + + nbconnection + + "/" + + peers.length + + ") :" + ); + } + else { + System.out.println("No peer"); + } + + for (int i=0;i "); + String command = this.readInput(); + String[] args = command.split(" "); + + int argc = args.length; + if (argc == 0 || command.length() == 0) { + continue; + } + + if (argc == 1 && args[0].equals("files")) this.files(); + else if (argc == 1 && args[0].equals("help")) this.help(); + else if (argc == 1 && args[0].equals("peers")) this.peers(); + else if (argc == 2 && args[0].equals("search")) this.search(args[1]); + else if (argc == 1 && args[0].equals("stats")) this.stats(); + else if (argc == 1 && args[0].equals("exit")) break; + else { + System.out.println("Unknown command : " + command); + System.out.println("Try help"); + } + } + + System.out.println("Exiting"); + System.exit(0); + } + + /** + * Initialise l'interface utilisateur + */ + static public void init() + { + CLI cli = new CLI(); + cli.start(); + } +} diff --git a/centralised/src/client/ClientDownloadThread.java b/centralised/src/client/ClientDownloadThread.java index 6f4a430..ea2dbf0 100644 --- a/centralised/src/client/ClientDownloadThread.java +++ b/centralised/src/client/ClientDownloadThread.java @@ -18,7 +18,7 @@ class ClientDownloadThread extends Thread * Créer le thread de téléchargement * @param : le fichier a télécharger */ - ClientDownloadThread(FileShared file) + public ClientDownloadThread(FileShared file) { super(); _file = file; @@ -33,19 +33,20 @@ class ClientDownloadThread extends Thread public void retrievePeers() { try { + App.downloads.connectTracker(); String[] data = App.downloads.tracker.getFile(_file.getKey()); for (int i=0;i 0) { - Peer peer = App.peers.getByHash(hash); + while (it.hasNext()) { + String hash = (String)it.next(); + Peer peer = App.peers.getByHash(hash); + if (peer == null) { + _file.peers.remove(hash); + continue; + } + Buffermap buffermap = _file.peers.get(hash); + int[] indexes = _file.getBuffermap().getDownloadPieces(buffermap, max); + if (indexes.length > 0) { + try { byte[][] data = peer.socket.getPieces(_file.getKey(), indexes); for (int i=0;i 0) { if (!this.retrievePieces()) { + try { + Thread.sleep(5000); + } + catch (InterruptedException e) { + e.printStackTrace(); + } this.retrievePeers(); this.retrieveBuffermap(); } } - + System.out.println("Completing " + _file.getName() + " ..."); App.files.transformToComplete(_file.getKey()); + System.out.println("Complete : " + _file.getName()); } } diff --git a/centralised/src/client/DownloadManager.java b/centralised/src/client/DownloadManager.java index 720cb16..338fe04 100644 --- a/centralised/src/client/DownloadManager.java +++ b/centralised/src/client/DownloadManager.java @@ -28,9 +28,34 @@ public void initServer() server.start(); } + /** + * Recontact le serveur + */ + public void connectTracker() + { + if ( + App.downloads.tracker == null || + !App.downloads.tracker.isConnected() || + App.downloads.tracker.isInputShutdown() || + App.downloads.tracker.isOutputShutdown() || + App.downloads.tracker.isClosed() + ) { + try { + String ip = (String)App.config.get("trackerIP"); + int port = (Integer)App.config.get("trackerPort"); + App.downloads.tracker = new Protocol(ip, port); + } + catch (Exception e) { + System.out.println("Unable to Recontact tracker"); + e.printStackTrace(); + } + } + } + /** * Initie la connexion avec le tracker * Démarre les threads de téléchargement des fichiers + * et de mise à jour */ public void initDownloads() { @@ -51,36 +76,39 @@ public void initDownloads() ClientDownloadThread client = new ClientDownloadThread(files[i]); client.start(); } + + UpdateThread update = new UpdateThread(); + update.start(); } /** - * Recherche et initie le téléchargement - * de fichier + * Recherche un fichier auprès du tracker * @param filename : nom du fichier à rechercher */ - public void search(String filename) + public String[] search(String filename) { try { + App.downloads.connectTracker(); String[] data = App.downloads.tracker.look(filename); - - for (int i=0;i(); + _files = new ConcurrentHashMap(); } /** @@ -47,7 +48,12 @@ private void scanCompleteDir() for (int i=0;i(); + this.peers = new ConcurrentHashMap(); + + this.downrate = new RateCounter(); + this.uprate = new RateCounter(); if (name.endsWith((String)App.config.get("tmpExtension"))) { _iscomplete = false; @@ -114,7 +130,10 @@ public FileShared(String name, String key, int size, int piecesize) if ((_size % _piecesize) > 0) nbpieces++; _buffermap = new Buffermap(nbpieces, false); - this.peers = new HashMap(); + this.peers = new ConcurrentHashMap(); + + this.downrate = new RateCounter(); + this.uprate = new RateCounter(); this.initHeaderTmpFile(); } @@ -494,7 +513,7 @@ synchronized private void writePieceTmpFile(byte[] piece, int num) if (piece.length > _piecesize) { throw new IllegalArgumentException(); } - + try { RandomAccessFile writer_tmp = new RandomAccessFile(this, "rw"); FileChannel writer = writer_tmp.getChannel(); diff --git a/centralised/src/client/Peer.java b/centralised/src/client/Peer.java index 25b3c19..cbc7443 100644 --- a/centralised/src/client/Peer.java +++ b/centralised/src/client/Peer.java @@ -22,14 +22,38 @@ class Peer */ public Protocol socket; + /** + * Compteur de débit descendant + */ + public RateCounter downrate; + /** * Créer et initialise de pair */ - public Peer(String ip, int port) throws UnknownHostException, IOException + public Peer(String ip, int port) { _ip = ip; _port = port; - socket = new Protocol(ip, port); + this.downrate = new RateCounter(); + } + + /** + * Connecte ou reconnecte la socket interne + * Renvoi true si la connexion est établie, false sinon + */ + public boolean connect() + { + try { + socket = new Protocol(_ip, _port); + return true; + } + catch (Exception e) { + System.out.println("Unable to contact peer : " + + this.getHash() + ); + e.printStackTrace(); + return false; + } } /** @@ -49,5 +73,47 @@ public static String computeHash(String ip, int port) { return ip + ":" + port; } + + /** + * Indique si la conexion intenre est active + */ + public boolean isConnected() + { + return (socket != null) && + socket.isConnected() && + !socket.isInputShutdown() && + !socket.isOutputShutdown() && + !socket.isClosed(); + } + + /** + * Ferme la connexion + */ + public void closeConnection() + { + try { + this.socket.close(); + } + catch (Exception e) { + System.out.println("Unable to close connection : " + this.getHash()); + e.printStackTrace(); + } + } + + /** + * Renvoi l'adresse ip + */ + public String getIP() + { + return _ip; + } + + /** + * Renvoi le port + */ + public int getPort() + { + return _port; + } } diff --git a/centralised/src/client/PeerManager.java b/centralised/src/client/PeerManager.java index 20d52da..92913d3 100644 --- a/centralised/src/client/PeerManager.java +++ b/centralised/src/client/PeerManager.java @@ -8,6 +8,7 @@ import java.util.*; import java.net.*; import java.io.IOException; +import java.util.concurrent.*; class PeerManager { @@ -21,7 +22,7 @@ class PeerManager */ public PeerManager() { - _peers = new HashMap(); + _peers = new ConcurrentHashMap(); } /** @@ -29,15 +30,19 @@ public PeerManager() * Test si la connexion est toujours valide * @return Peer ou null si le pair demandé n'est pas joignable */ - public Peer getByHash(String hash) + synchronized public Peer getByHash(String hash) { Peer peer = _peers.get(hash); - if (peer.socket.isConnected()) { - return _peers.get(hash); + if (peer.isConnected()) { + return peer; } else { - _peers.remove(peer.getHash()); - return null; + if (this.canConnectPeer() && peer.connect()) { + return peer; + } + else { + return null; + } } } @@ -48,27 +53,75 @@ public Peer getByHash(String hash) * @return String contenant le hash du Peer * si succès, renvoi null sinon */ - public String add(String ip, int port) + synchronized public String add(String ip, int port) + { + String hash = Peer.computeHash(ip, port); + if (_peers.get(hash) == null) { + Peer peer = new Peer(ip, port); + _peers.put(peer.getHash(), peer); + } + + return hash; + } + + /** + * Retourne tout les pairs connus + */ + synchronized public Peer[] getAllPeers() { - try { - String hash = Peer.computeHash(ip, port); - if (_peers.get(hash) == null) { - Peer peer = new Peer(ip, port); - _peers.put(peer.getHash(), peer); + Peer[] peers = new Peer[_peers.size()]; + int i = 0; + + Set keys = _peers.keySet(); + Iterator it = keys.iterator(); + while (it.hasNext()){ + String hash = (String)it.next(); + peers[i] = _peers.get(hash); + i++; + } + + return peers; + } + + /** + * Retourne le nombre connexion actuellement ouverte + * vers d'autres pairs en mode client + */ + public int nbConnectedPeers() + { + Peer[] peers = this.getAllPeers(); + int count = 0; + + for (int i=0;i groups = new ArrayList(); - + int offset = readBytesToPattern(reader, 0, _interested, null, groups); if (!groups.get(0).equals(key)) { throw new IOException("Protocol error"); } - + FileShared file = App.files.getByKey(key); int buffermap_size = file.buffermapSize(); int nbpieces = file.nbPieces(); byte[] buffermap_buf = reader.read(offset, buffermap_size); - return new Buffermap(buffermap_buf, nbpieces); + Buffermap buffermap = new Buffermap(buffermap_buf, nbpieces); + return buffermap; } /** @@ -267,6 +268,7 @@ synchronized public byte[][] getPieces(String key, int[] indexes) throws IOExcep groups.remove(0); offset += tmp; data[index] = reader.read(offset, piecesize); + offset += piecesize; } return data; @@ -337,6 +339,9 @@ synchronized public void update() throws IOException readBytesToPattern(reader, 0, _update, null, null); } + /** + * Lis et parse une commande serveur + */ public void serverReadAndDispatch() throws IOException { String msg_interested = "interested"; @@ -401,7 +406,11 @@ public void serverReadAndDispatch() throws IOException if (b == -1) throw new IOException("Connection ended"); if ((char)b != ' ') throw new IOException("Protocol error"); - FileShared file = App.files.getByKey(key); + FileShared file = App.files.hasByKey(key); + if (file == null) { + return; + } + int len = 0; int buffermap_size = file.buffermapSize(); byte[] buffermap_buf = new byte[buffermap_size]; @@ -440,15 +449,23 @@ private void data_server(String key, int[] indexes) throws IOException { FileShared file = App.files.getByKey(key); - String query = "data " + key + "["; + String query = "data " + key + " ["; byte[] buffer = query.getBytes(); for (int i=0;i 0) { diff --git a/centralised/src/client/RateCounter.java b/centralised/src/client/RateCounter.java new file mode 100644 index 0000000..40e6b86 --- /dev/null +++ b/centralised/src/client/RateCounter.java @@ -0,0 +1,74 @@ +/** + * Représente un compteur de débit + */ + +import java.io.*; + +class RateCounter +{ + /** + * date de la dernière réinitialisation + */ + private long _timestart; + + /** + * Compteur de débit + */ + private long _counter; + + /** + * Valeur du débit + */ + private float _rate; + + /** + * Créé et initialide le compteur + */ + public RateCounter() + { + _timestart = System.currentTimeMillis(); + _counter = 0; + _rate = 0; + } + + /** + * Founit au compteur le débit échangé + * @param : la quantité de donnée échangée + */ + synchronized public void tick(int datasize) + { + long current = System.currentTimeMillis(); + int mean = (Integer)App.config.get("rateMeanTime"); + + if (current - _timestart > mean) { + if (current - _timestart > 2*mean) { + _rate = 0; + } + else { + _rate = (float)Math.floor(100*(float)_counter/(float)mean)/100; + } + _timestart = current; + _counter = 0; + } + + _counter += datasize; + } + + /** + * Renvoi la valeur actuelle du débit + * en Ko/s + */ + synchronized public float getRate() + { + long current = System.currentTimeMillis(); + int mean = (Integer)App.config.get("rateMeanTime"); + + if (current - _timestart > 2*mean) { + return 0; + } + else { + return _rate; + } + } +} + diff --git a/centralised/src/client/ServerConnectionThread.java b/centralised/src/client/ServerConnectionThread.java index 54553f7..8ce1354 100644 --- a/centralised/src/client/ServerConnectionThread.java +++ b/centralised/src/client/ServerConnectionThread.java @@ -37,7 +37,6 @@ public void run() } catch (Exception e) { - /*TODO fermer la socket */ System.out.println("End server connection :"); e.printStackTrace(); } diff --git a/centralised/src/client/UpdateThread.java b/centralised/src/client/UpdateThread.java new file mode 100644 index 0000000..7fe32b7 --- /dev/null +++ b/centralised/src/client/UpdateThread.java @@ -0,0 +1,45 @@ +/** + * Représente le thread de mise à jour + * régulière du tracker et des pairs + */ + +import java.io.*; +import java.util.*; +import java.lang.*; + +class UpdateThread extends Thread +{ + /** + * Contruit et initialise le thread + */ + public UpdateThread() { + super(); + } + + /** + * Fonction principale du thread + * Mise à jour régulière du tracker + */ + public void run() + { + do { + try { + int delay = (Integer)App.config.get("updateTime"); + Thread.sleep(delay); + } + catch (InterruptedException e) { + e.printStackTrace(); + } + try { + App.downloads.connectTracker(); + App.downloads.tracker.update(); + } + catch (Exception e) + { + System.out.println("Unable to update tracker"); + e.printStackTrace(); + } + } while (true); + } +} +