├── nodes ├── start.sh ├── settings.properties ├── src ├── KPack │ ├── Packets │ │ ├── PingReply.java │ │ ├── PingRequest.java │ │ ├── DeleteRequest.java │ │ ├── Packet.java │ │ ├── FindNodeReply.java │ │ ├── FindValueRequest.java │ │ ├── FindNodeRequest.java │ │ ├── FindValueReply.java │ │ └── StoreRequest.java │ ├── Files │ │ ├── KadFileInterf.java │ │ ├── KadFileMapInterf.java │ │ ├── KadFile.java │ │ └── KadFileMap.java │ ├── Exceptions │ │ ├── InvalidSettingsException.java │ │ ├── NotAValidParentException.java │ │ ├── FileNotKnownException.java │ │ └── AlreadyInstancedException.java │ ├── Tree │ │ ├── Node.java │ │ ├── TreeNode.java │ │ ├── Bucket.java │ │ └── RoutingTree.java │ ├── KademliaInterf.java │ ├── FixedKadNode.java │ ├── KadNode.java │ ├── UserInterface │ │ ├── TreeUI.form │ │ ├── TreeUI.java │ │ └── UserInterface.java │ ├── Tupla.java │ └── Kademlia.java └── Main.java ├── KademliaFileStorage.iml ├── LICENSE └── README.md /nodes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/apontini/KademliaFileStorage/HEAD/nodes -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | cd ./src/ 4 | javac Main.java 5 | cd .. 6 | java -classpath ./src/ Main 7 | 8 | 9 | -------------------------------------------------------------------------------- /settings.properties: -------------------------------------------------------------------------------- 1 | #impostazioni per Kademlia 2 | #i valori sono provvisori, a scopo dimostrativo/di test 3 | port=1337 4 | fileRefreshThreadSleep=20000 5 | fileRefreshWait=100000 6 | bucketRefreshWait=200000 7 | socketTimeout=20000 8 | -------------------------------------------------------------------------------- /src/KPack/Packets/PingReply.java: -------------------------------------------------------------------------------- 1 | package KPack.Packets; 2 | 3 | import KPack.KadNode; 4 | import java.io.Serializable; 5 | 6 | public class PingReply extends Packet implements Serializable { 7 | 8 | public PingReply(KadNode source, KadNode dest) 9 | { 10 | super(source, dest); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/KPack/Files/KadFileInterf.java: -------------------------------------------------------------------------------- 1 | package KPack.Files; 2 | 3 | import java.math.BigInteger; 4 | 5 | public interface KadFileInterf { 6 | 7 | BigInteger getFileID(); 8 | 9 | boolean isRedundant(); 10 | 11 | String getFileName(); 12 | 13 | String getPath(); 14 | 15 | long getLastRefresh(); 16 | } 17 | -------------------------------------------------------------------------------- /src/KPack/Packets/PingRequest.java: -------------------------------------------------------------------------------- 1 | package KPack.Packets; 2 | 3 | import KPack.KadNode; 4 | import java.io.Serializable; 5 | 6 | public class PingRequest extends Packet implements Serializable { 7 | 8 | public PingRequest(KadNode source, KadNode dest) 9 | { 10 | super(source, dest); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/KPack/Exceptions/InvalidSettingsException.java: -------------------------------------------------------------------------------- 1 | package KPack.Exceptions; 2 | 3 | public class InvalidSettingsException extends Exception 4 | { 5 | public InvalidSettingsException() 6 | { 7 | } 8 | 9 | public InvalidSettingsException(String message) 10 | { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/KPack/Exceptions/NotAValidParentException.java: -------------------------------------------------------------------------------- 1 | package KPack.Exceptions; 2 | 3 | public class NotAValidParentException extends Exception { 4 | 5 | public NotAValidParentException() 6 | { 7 | } 8 | 9 | public NotAValidParentException(String message) 10 | { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/KPack/Exceptions/FileNotKnownException.java: -------------------------------------------------------------------------------- 1 | package KPack.Exceptions; 2 | 3 | public class FileNotKnownException extends Exception { 4 | 5 | // Parameterless Constructor 6 | public FileNotKnownException() 7 | { 8 | } 9 | 10 | // Constructor that accepts a message 11 | public FileNotKnownException(String message) 12 | { 13 | super(message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/KPack/Files/KadFileMapInterf.java: -------------------------------------------------------------------------------- 1 | package KPack.Files; 2 | 3 | import java.math.BigInteger; 4 | import java.util.Iterator; 5 | 6 | public interface KadFileMapInterf { 7 | 8 | void add(KadFile file); 9 | 10 | void remove(KadFile file); 11 | 12 | void remove(BigInteger ID); 13 | 14 | KadFile get(BigInteger i); 15 | 16 | void clearAll(); 17 | 18 | int size(); 19 | } 20 | -------------------------------------------------------------------------------- /src/KPack/Exceptions/AlreadyInstancedException.java: -------------------------------------------------------------------------------- 1 | package KPack.Exceptions; 2 | 3 | public class AlreadyInstancedException extends Exception { 4 | 5 | // Parameterless Constructor 6 | public AlreadyInstancedException() 7 | { 8 | } 9 | 10 | // Constructor that accepts a message 11 | public AlreadyInstancedException(String message) 12 | { 13 | super(message); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/KPack/Packets/DeleteRequest.java: -------------------------------------------------------------------------------- 1 | package KPack.Packets; 2 | 3 | import KPack.Files.KadFile; 4 | import KPack.KadNode; 5 | import java.io.Serializable; 6 | 7 | public class DeleteRequest extends Packet implements Serializable { 8 | 9 | private KadFile kf; 10 | 11 | public DeleteRequest(KadFile kf, KadNode source, KadNode dest) 12 | { 13 | super(source, dest); 14 | this.kf = kf; 15 | } 16 | 17 | public KadFile getFile() 18 | { 19 | return kf; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/KPack/Tree/Node.java: -------------------------------------------------------------------------------- 1 | package KPack.Tree; 2 | 3 | public abstract class Node { 4 | 5 | private Node parent; 6 | 7 | public synchronized Node getParent() 8 | { 9 | return parent; 10 | } 11 | 12 | public synchronized void setParent(Node parent) 13 | { 14 | this.parent = parent; 15 | } 16 | 17 | public synchronized int getDepth() 18 | { 19 | if (parent == null) 20 | { 21 | return 0; 22 | } 23 | return parent.getDepth() + 1; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /KademliaFileStorage.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/KPack/Tree/TreeNode.java: -------------------------------------------------------------------------------- 1 | package KPack.Tree; 2 | 3 | public class TreeNode extends Node { 4 | 5 | //SOLO nodo interno 6 | private Node left; 7 | private Node right; 8 | 9 | public synchronized Node getLeft() 10 | { 11 | return left; 12 | } 13 | 14 | public synchronized Node getRight() 15 | { 16 | return right; 17 | } 18 | 19 | public synchronized void setLeft(Node left) 20 | { 21 | this.left = left; 22 | } 23 | 24 | public synchronized void setRight(Node right) 25 | { 26 | this.right = right; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/KPack/Packets/Packet.java: -------------------------------------------------------------------------------- 1 | package KPack.Packets; 2 | 3 | import KPack.KadNode; 4 | import java.io.Serializable; 5 | 6 | public class Packet implements Serializable{ 7 | 8 | private final KadNode source; 9 | private final KadNode dest; 10 | 11 | public Packet(KadNode source, KadNode dest) 12 | { 13 | this.source = source; 14 | this.dest = dest; 15 | } 16 | 17 | public KadNode getSourceKadNode() 18 | { 19 | return source; 20 | } 21 | 22 | public KadNode getDestKadNode() 23 | { 24 | return dest; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/KPack/Packets/FindNodeReply.java: -------------------------------------------------------------------------------- 1 | package KPack.Packets; 2 | 3 | import KPack.KadNode; 4 | 5 | import java.io.Serializable; 6 | import java.math.BigInteger; 7 | import java.util.List; 8 | 9 | public class FindNodeReply extends Packet implements Serializable { 10 | 11 | private BigInteger targetID; 12 | private List lkn; 13 | 14 | public FindNodeReply(BigInteger targetID, KadNode sourceKN, KadNode destKN, List lkn) 15 | { 16 | super(sourceKN, destKN); 17 | this.targetID = targetID; 18 | this.lkn = lkn; 19 | } 20 | 21 | public BigInteger getTargetID() 22 | { 23 | return targetID; 24 | } 25 | 26 | public List getList() 27 | { 28 | return lkn; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/KPack/Packets/FindValueRequest.java: -------------------------------------------------------------------------------- 1 | package KPack.Packets; 2 | 3 | import KPack.KadNode; 4 | import java.io.Serializable; 5 | import java.math.BigInteger; 6 | 7 | public class FindValueRequest extends Packet implements Serializable { 8 | 9 | private BigInteger fileID; 10 | private boolean contentRequested; 11 | 12 | public FindValueRequest(BigInteger fileID, KadNode source, KadNode dest, boolean contentRequested) 13 | { 14 | super(source, dest); 15 | this.fileID = fileID; 16 | this.contentRequested = contentRequested; 17 | } 18 | 19 | public BigInteger getFileID() 20 | { 21 | return fileID; 22 | } 23 | 24 | public boolean isContentRequested() 25 | { 26 | return contentRequested; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/KPack/KademliaInterf.java: -------------------------------------------------------------------------------- 1 | package KPack; 2 | 3 | import KPack.Exceptions.FileNotKnownException; 4 | import KPack.Files.KadFile; 5 | 6 | import java.io.FileNotFoundException; 7 | import java.math.BigInteger; 8 | import java.security.InvalidParameterException; 9 | import java.util.List; 10 | 11 | public interface KademliaInterf 12 | { 13 | 14 | BigInteger getNodeID(); 15 | 16 | List getFileList(); 17 | 18 | boolean ping(KadNode node); 19 | 20 | Object findValue(BigInteger fileID, boolean returnContent); 21 | 22 | List findNode(BigInteger nodeID); 23 | 24 | void store(String filepath) throws FileNotFoundException, InvalidParameterException; 25 | 26 | void delete(BigInteger ID) throws FileNotKnownException; 27 | 28 | KadNode getMyNode(); 29 | } 30 | -------------------------------------------------------------------------------- /src/KPack/Packets/FindNodeRequest.java: -------------------------------------------------------------------------------- 1 | package KPack.Packets; 2 | 3 | import KPack.KadNode; 4 | 5 | import java.io.Serializable; 6 | import java.math.BigInteger; 7 | 8 | public class FindNodeRequest extends Packet implements Serializable { 9 | 10 | private BigInteger targetID; 11 | private boolean track; //se è falsa, il nodo che riceve la richiesta non aggiorna il bucket relativo all'ID 12 | 13 | public FindNodeRequest(BigInteger targetID, KadNode sourceKN, KadNode destKN, boolean track) 14 | { 15 | super(sourceKN, destKN); 16 | this.targetID = targetID; 17 | this.track = track; 18 | } 19 | 20 | public BigInteger getTargetID() 21 | { 22 | return targetID; 23 | } 24 | 25 | public boolean isTracked() 26 | { 27 | return track; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/KPack/FixedKadNode.java: -------------------------------------------------------------------------------- 1 | package KPack; 2 | 3 | import java.math.BigInteger; 4 | import java.net.InetAddress; 5 | import java.net.UnknownHostException; 6 | 7 | public class FixedKadNode extends KadNode { 8 | 9 | private String name; 10 | private String indirizzo; 11 | 12 | public FixedKadNode(String indirizzo, int port, BigInteger ID, String name) 13 | { 14 | super(indirizzo, port, ID); 15 | this.name = name; 16 | this.indirizzo = indirizzo; 17 | } 18 | 19 | public String getName() 20 | { 21 | return name; 22 | } 23 | 24 | public KadNode getKadNode() 25 | { 26 | try 27 | { 28 | InetAddress inAddr = InetAddress.getByName(indirizzo); 29 | return new KadNode(inAddr.getHostAddress(), super.getPort(), super.getNodeID()); 30 | } 31 | catch (UnknownHostException ex) 32 | { 33 | return null; 34 | } 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Lahace 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/KPack/KadNode.java: -------------------------------------------------------------------------------- 1 | package KPack; 2 | 3 | import java.io.Serializable; 4 | import java.math.BigInteger; 5 | import java.net.InetAddress; 6 | import java.net.UnknownHostException; 7 | import java.util.Objects; 8 | 9 | public class KadNode implements Serializable { 10 | 11 | private InetAddress ip; 12 | private int port; 13 | private BigInteger nodeID; 14 | 15 | public KadNode(String ipString, int port, BigInteger ID) 16 | { 17 | try 18 | { 19 | ip = InetAddress.getByName(ipString); 20 | } 21 | catch (UnknownHostException uoe) 22 | { 23 | uoe.printStackTrace(); 24 | } 25 | 26 | this.port = port; 27 | this.nodeID = ID; 28 | } 29 | 30 | public InetAddress getIp() 31 | { 32 | return ip; 33 | } 34 | 35 | public int getPort() 36 | { 37 | return port; 38 | } 39 | 40 | public BigInteger getNodeID() 41 | { 42 | return nodeID; 43 | } 44 | 45 | @Override 46 | public boolean equals(Object o) 47 | { 48 | if (this == o) 49 | { 50 | return true; 51 | } 52 | if (o == null || getClass() != o.getClass()) 53 | { 54 | return false; 55 | } 56 | KadNode kadNode = (KadNode) o; 57 | return Objects.equals(nodeID, kadNode.nodeID); 58 | } 59 | 60 | @Override 61 | public String toString() 62 | { 63 | return "KadNode{" 64 | + "ip=" + ip 65 | + ", port=" + port 66 | + ", nodeID=" + nodeID 67 | + '}'; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/KPack/Packets/FindValueReply.java: -------------------------------------------------------------------------------- 1 | package KPack.Packets; 2 | 3 | import KPack.Files.KadFileInterf; 4 | import KPack.KadNode; 5 | 6 | import java.io.IOException; 7 | import java.io.Serializable; 8 | import java.math.BigInteger; 9 | import java.nio.file.Files; 10 | import java.nio.file.Path; 11 | import java.nio.file.Paths; 12 | import java.util.List; 13 | 14 | public class FindValueReply extends Packet implements Serializable { 15 | 16 | private final String fileName; 17 | private BigInteger fileID; 18 | private byte[] content; 19 | private List lkn; 20 | 21 | public FindValueReply(BigInteger fileID, KadFileInterf kf, KadNode source, KadNode dest) 22 | { 23 | super(source, dest); 24 | this.fileID = fileID; 25 | this.lkn = lkn; 26 | if (kf == null) 27 | { 28 | content = null; 29 | fileName = null; 30 | } 31 | else 32 | { 33 | this.fileName = kf.getFileName(); 34 | 35 | Path path = Paths.get(kf.getPath()+kf.getFileName()); 36 | try 37 | { 38 | content = Files.readAllBytes(path); 39 | } 40 | catch (IOException e) 41 | { 42 | e.printStackTrace(); 43 | } 44 | } 45 | } 46 | 47 | public BigInteger getFileID() 48 | { 49 | return fileID; 50 | } 51 | 52 | public byte[] getContent() 53 | { 54 | return content; 55 | } 56 | 57 | public String getFileName() 58 | { 59 | return fileName; 60 | } 61 | 62 | public List getListKadNode() 63 | { 64 | return lkn; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/KPack/Packets/StoreRequest.java: -------------------------------------------------------------------------------- 1 | package KPack.Packets; 2 | 3 | import KPack.Files.KadFileInterf; 4 | import KPack.KadNode; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.io.Serializable; 9 | import java.math.BigInteger; 10 | import java.nio.file.Files; 11 | import java.nio.file.Path; 12 | import java.nio.file.Paths; 13 | import java.security.InvalidParameterException; 14 | 15 | public class StoreRequest extends Packet implements Serializable { 16 | 17 | private BigInteger fileID; 18 | private String fileName; 19 | private byte[] content; 20 | 21 | public StoreRequest(KadFileInterf kf, KadNode source, KadNode dest) throws IOException 22 | { 23 | super(source, dest); 24 | this.fileID = kf.getFileID(); 25 | this.fileName = kf.getFileName(); 26 | //Orribile ma temporanea 27 | if (!((new File(kf.getPath())).isDirectory())) 28 | { 29 | throw new InvalidParameterException("Errore nella composizione del KadFile, nel path"); 30 | } 31 | if ((new File(kf.getPath() + File.separator + kf.getFileName())).isDirectory()) 32 | { 33 | throw new InvalidParameterException("Errore nella composizione del KadFile, il file è una directory"); 34 | } 35 | Path path = Paths.get(kf.getPath() + File.separator + kf.getFileName()); 36 | content = Files.readAllBytes(path); 37 | } 38 | 39 | public BigInteger getFileID() 40 | { 41 | return fileID; 42 | } 43 | 44 | public String getFileName() 45 | { 46 | return fileName; 47 | } 48 | 49 | public byte[] getContent() 50 | { 51 | return content; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/KPack/UserInterface/TreeUI.form: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 |
37 | -------------------------------------------------------------------------------- /src/KPack/Files/KadFile.java: -------------------------------------------------------------------------------- 1 | package KPack.Files; 2 | 3 | import java.io.Serializable; 4 | import java.math.BigInteger; 5 | import java.util.Objects; 6 | 7 | public class KadFile implements KadFileInterf, Serializable { 8 | 9 | //deve mantenere singolo file 10 | private BigInteger fileID; 11 | private boolean redundant; //false se il file è mio o true se mi è stato dato grazie al protocollo della rete Kademlia 12 | private String fileName; 13 | private String path; 14 | private long lastRefresh; 15 | 16 | public KadFile(BigInteger fileID, boolean redundant, String fileName, String path) 17 | { 18 | this.fileID = fileID; 19 | this.redundant = redundant; 20 | this.fileName = fileName; 21 | this.path = path; 22 | if(redundant) lastRefresh = System.currentTimeMillis(); 23 | } 24 | 25 | public BigInteger getFileID() 26 | { 27 | return fileID; 28 | } 29 | 30 | public boolean isRedundant() 31 | { 32 | return redundant; 33 | } 34 | 35 | public String getFileName() 36 | { 37 | return fileName; 38 | } 39 | 40 | public String getPath() 41 | { 42 | return path; 43 | } 44 | 45 | public long getLastRefresh() { return lastRefresh; } 46 | 47 | public void setLastRefresh(long lastRefresh) { this.lastRefresh = lastRefresh;} 48 | 49 | @Override 50 | public String toString() 51 | { 52 | return "KadFile{" 53 | + "fileID=" + fileID 54 | + ", redundant=" + redundant 55 | + ", fileName='" + fileName + '\'' 56 | + ", path='" + path + '\'' 57 | + '}'; 58 | } 59 | 60 | @Override 61 | public boolean equals(Object obj) 62 | { 63 | if (this == obj) 64 | { 65 | return true; 66 | } 67 | if (obj == null) 68 | { 69 | return false; 70 | } 71 | if (getClass() != obj.getClass()) 72 | { 73 | return false; 74 | } 75 | final KadFile other = (KadFile) obj; 76 | if (!Objects.equals(this.fileName, other.fileName)) 77 | { 78 | return false; 79 | } 80 | if (!Objects.equals(this.fileID, other.fileID)) 81 | { 82 | return false; 83 | } 84 | return true; 85 | } 86 | 87 | 88 | } 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kademlia File Storage 2 | 3 | Kademlia File Storage is a project created for the Distributed Systems course in Computer Science Engineering at University of Padua. 4 | It uses *Kademlia* DHT to distribute file to other peers for storage purpose. 5 | Our goal is to achieve the most permanent file storage solution on a p2p network. 6 | *We will not maintain this project once it's completed (with regards to our course objectives)*. 7 | 8 | #### How does it work? 9 | When instantiating a node, a FIND_NODE RPC is performed on a list of a handful nodes (which is serialized in the `nodes` file) to know at most K nodes which are the closest to our node. 10 | This file is serialized by the `writeFixedNodes` method in `Kademlia.java`, if you need to change those nodes for some implementation/change/whatever purposes, please check that. 11 | From then, all other RPCs can be called to interact with the network. 12 | While a node is instantiated, it will receive files from other peers which are performing a *STORE* RPC on the network. These files are frequently refreshed to quickly react to changes occuring in the network, ensuring file persistance for the longest time possible. 13 | 14 | #### How to use it 15 | As you can see in `Main.java` you can simply join a network by creating an instance of the `Kademlia` as follows: 16 | ``` 17 | Kademlia myNode = new Kademlia(); 18 | ``` 19 | 20 | You can then start using Kademlia 4 basic RPCs (*FIND_NODE*, *PING*, *FIND_VALUE* and *STORE*) and an extra RPC (*DELETE*), added by us, to interact with the p2p network and store files. 21 | That's it! 22 | 23 | If you wish to test it with our provided implementation, just run the `start.sh` file found in the main directory. 24 | This will build all Java classes needed and will run `Main.java`. 25 | 26 | #### Settings 27 | For testing purposes, we added some settings inside the `settings.properties` file. These are: 28 | - **port**: the port address which will be used (MUST be forwarded if behind a NAT). 29 | - **fileRefreshThreadSleep**: amount of time (in milliseconds) between one file refresh sequence and another. 30 | - **fileRefreshWait**: amount of time (in milliseconds) to wait before a file can be refreshed. 31 | - **bucketRefreshWait**: amount of time (in milliseconds) between one bucket refresh and another. 32 | - **socketTimeout**: self-explainatory (in milliseconds). 33 | 34 | Based on [Petar Maymounkov and David Mazières paper](https://pdos.csail.mit.edu/~petar/papers/maymounkov-kademlia-lncs.pdf). 35 | Clarifications were found in a [presentation](https://docs.google.com/presentation/d/11qGZlPWu6vEAhA7p3qsQaQtWH7KofEC9dMeBFZ1gYeA/edit#slide=id.g1718cc2bc_08645) made by Tristan Slominski (@tristanls) 36 | -------------------------------------------------------------------------------- /src/KPack/Tupla.java: -------------------------------------------------------------------------------- 1 | package KPack; 2 | 3 | import java.io.Serializable; 4 | import java.math.BigInteger; 5 | import java.util.Objects; 6 | 7 | public class Tupla implements Serializable { 8 | 9 | private K key; 10 | private V value; 11 | 12 | public Tupla(K key, V value) 13 | { 14 | this.key = key; 15 | this.value = value; 16 | } 17 | 18 | public K getKey() 19 | { 20 | return key; 21 | } 22 | 23 | public V getValue() 24 | { 25 | return value; 26 | } 27 | 28 | public void setKey(K key) 29 | { 30 | this.key = key; 31 | } 32 | 33 | public void setValue(V value) 34 | { 35 | this.value = value; 36 | } 37 | 38 | @Override 39 | public boolean equals(Object obj) 40 | { 41 | if (this == obj) 42 | { 43 | return true; 44 | } 45 | if (obj == null) 46 | { 47 | return false; 48 | } 49 | if (obj instanceof Tupla) 50 | { 51 | if (key instanceof BigInteger && ((Tupla) obj).getKey() instanceof BigInteger) 52 | { 53 | if (!((BigInteger) key).equals((BigInteger) (((Tupla) obj).getKey()))) 54 | { 55 | return false; 56 | } 57 | } 58 | else 59 | { 60 | return false; 61 | } 62 | if (value instanceof Boolean && ((Tupla) obj).getValue() instanceof Boolean) 63 | { 64 | if (!((Boolean) value).equals((Boolean) (((Tupla) obj).getValue()))) 65 | { 66 | return false; 67 | } 68 | } 69 | else 70 | { 71 | return false; 72 | } 73 | return true; 74 | } 75 | if (getClass() != obj.getClass()) 76 | { 77 | return false; 78 | } 79 | final Tupla other = (Tupla) obj; 80 | if (!Objects.equals(this.key, other.key)) 81 | { 82 | return false; 83 | } 84 | if (!Objects.equals(this.value, other.value)) 85 | { 86 | return false; 87 | } 88 | return true; 89 | } 90 | 91 | @Override 92 | public int hashCode() 93 | { 94 | int hash = 1; 95 | if (key instanceof BigInteger) 96 | { 97 | hash += ((BigInteger) key).hashCode(); 98 | } 99 | else 100 | { 101 | hash += key.hashCode(); 102 | } 103 | if (value instanceof Boolean) 104 | { 105 | hash += ((Boolean) value).hashCode(); 106 | } 107 | else 108 | { 109 | hash += value.hashCode(); 110 | } 111 | return hash; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/KPack/UserInterface/TreeUI.java: -------------------------------------------------------------------------------- 1 | package KPack.UserInterface; 2 | 3 | import KPack.KadNode; 4 | import KPack.Kademlia; 5 | import KPack.Tree.Bucket; 6 | import KPack.Tree.Node; 7 | import KPack.Tree.RoutingTree; 8 | import KPack.Tree.TreeNode; 9 | import java.awt.Dimension; 10 | import static java.lang.Thread.sleep; 11 | import java.util.Iterator; 12 | import javax.swing.JTree; 13 | import javax.swing.tree.DefaultMutableTreeNode; 14 | 15 | public class TreeUI extends javax.swing.JFrame { 16 | 17 | private JTree tree; 18 | private RoutingTree routingTree; 19 | private Kademlia thisNode; 20 | 21 | public TreeUI(RoutingTree rt, Kademlia thisNode) 22 | { 23 | routingTree = rt; 24 | this.thisNode = thisNode; 25 | Node rootNode = routingTree.getRoot(); 26 | DefaultMutableTreeNode rootTree = new DefaultMutableTreeNode(""); 27 | recursiveTree(rootNode, rootTree); 28 | 29 | tree = new JTree(rootTree); 30 | add(tree); 31 | 32 | this.setPreferredSize(new Dimension(500, 500)); 33 | this.setSize(new Dimension(500, 500)); 34 | this.setTitle("Routing Tree"); 35 | this.pack(); 36 | this.setVisible(true); 37 | 38 | new Thread(() -> 39 | { 40 | while (true) 41 | { 42 | try 43 | { 44 | sleep(750); 45 | } 46 | catch (InterruptedException ex) 47 | { 48 | ex.printStackTrace(); 49 | } 50 | 51 | updateTree(); 52 | } 53 | }).start(); 54 | } 55 | 56 | @SuppressWarnings("unchecked") 57 | // //GEN-BEGIN:initComponents 58 | private void initComponents() 59 | { 60 | 61 | setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); 62 | setTitle("Routing Tree"); 63 | 64 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); 65 | getContentPane().setLayout(layout); 66 | layout.setHorizontalGroup( 67 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 68 | .addGap(0, 855, Short.MAX_VALUE) 69 | ); 70 | layout.setVerticalGroup( 71 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 72 | .addGap(0, 629, Short.MAX_VALUE) 73 | ); 74 | 75 | pack(); 76 | }// //GEN-END:initComponents 77 | 78 | private void updateTree() 79 | { 80 | remove(tree); 81 | Node rootNode = routingTree.getRoot(); 82 | DefaultMutableTreeNode rootTree = new DefaultMutableTreeNode(""); 83 | 84 | recursiveTree(rootNode, rootTree); 85 | 86 | tree = new JTree(rootTree); 87 | add(tree); 88 | 89 | for (int i = 0; i < tree.getRowCount(); i++) 90 | { 91 | tree.expandRow(i); 92 | } 93 | this.pack(); 94 | 95 | } 96 | 97 | private void recursiveTree(Node n, DefaultMutableTreeNode dmt) 98 | { 99 | if (n instanceof Bucket) 100 | { 101 | Bucket b = (Bucket) n; 102 | //System.out.println(b.toString()); 103 | synchronized (b) 104 | { 105 | Iterator ikn = b.iterator(); 106 | while (ikn.hasNext()) 107 | { 108 | KadNode kn = ikn.next(); 109 | DefaultMutableTreeNode treeNode = null; 110 | try 111 | { 112 | treeNode = new DefaultMutableTreeNode(Kademlia.intToBinary(kn.getNodeID()) + " (" + kn.getNodeID() + ") from: " + kn.getIp().toString() + ":" + kn.getPort() + "(Dist: " + thisNode.distanza(thisNode.getMyNode(), kn) + ")"); 113 | } 114 | catch(NullPointerException e) 115 | { 116 | treeNode = new DefaultMutableTreeNode(Kademlia.intToBinary(kn.getNodeID()) + " (" + kn.getNodeID() + ") from: " + kn.getIp().toString() + ":" + kn.getPort() + "(Dist: /)"); 117 | } 118 | dmt.add(treeNode); 119 | } 120 | } 121 | } 122 | else 123 | { 124 | TreeNode tn = (TreeNode) n; 125 | DefaultMutableTreeNode sx = new DefaultMutableTreeNode("1"); 126 | DefaultMutableTreeNode dx = new DefaultMutableTreeNode("0"); 127 | dmt.add(sx); 128 | dmt.add(dx); 129 | 130 | recursiveTree(tn.getLeft(), sx); 131 | recursiveTree(tn.getRight(), dx); 132 | 133 | } 134 | } 135 | // Variables declaration - do not modify//GEN-BEGIN:variables 136 | // End of variables declaration//GEN-END:variables 137 | } 138 | -------------------------------------------------------------------------------- /src/KPack/Files/KadFileMap.java: -------------------------------------------------------------------------------- 1 | package KPack.Files; 2 | 3 | import KPack.Kademlia; 4 | 5 | import java.io.*; 6 | import java.math.BigInteger; 7 | import java.util.HashMap; 8 | import java.util.function.BiConsumer; 9 | 10 | public class KadFileMap implements KadFileMapInterf { 11 | 12 | private HashMap fileMap; 13 | private Kademlia thisNode; 14 | private boolean serialized; 15 | 16 | public KadFileMap(Kademlia thisNode, boolean serialized) 17 | { 18 | fileMap = new HashMap<>(); 19 | if(serialized) fileMap = loadListFromFile(); 20 | this.thisNode = thisNode; 21 | this.serialized = serialized; 22 | } 23 | 24 | synchronized public void add(KadFile file) 25 | { 26 | fileMap.put(file.getFileID(), file); 27 | if(serialized) serializeMap(); 28 | } 29 | 30 | synchronized public void remove(KadFile file) 31 | { 32 | remove(file.getFileID()); 33 | } 34 | 35 | synchronized public void remove(BigInteger ID) 36 | { 37 | KadFile temp = fileMap.remove(ID); 38 | 39 | if(serialized) 40 | { 41 | serializeMap(); 42 | } 43 | else 44 | { 45 | new File(temp.getPath() + File.separator + temp.getFileName()).delete(); 46 | } 47 | } 48 | 49 | public void forEach(BiConsumer function) //Prendere il lock per usare questo metodo! 50 | { 51 | fileMap.forEach(function); 52 | } 53 | 54 | synchronized public void clearAll() 55 | { 56 | fileMap.clear(); 57 | } 58 | 59 | synchronized public KadFile get(BigInteger i) 60 | { 61 | return fileMap.get(i); 62 | } 63 | 64 | synchronized public int size() //Prendere il lock per ciclare su size! 65 | { 66 | return fileMap.size(); 67 | } 68 | 69 | private void serializeMap() 70 | { 71 | FileOutputStream fout = null; 72 | ObjectOutputStream oos = null; 73 | try 74 | { 75 | File temp = new File(thisNode.FILESPATH); 76 | if (!(temp.exists())) 77 | { 78 | temp.mkdir(); 79 | } 80 | File localFiles = new File(thisNode.FILESPATH + "index"); 81 | if (!(localFiles.exists())) 82 | { 83 | localFiles.createNewFile(); 84 | } 85 | 86 | fout = new FileOutputStream(thisNode.FILESPATH + "index"); 87 | oos = new ObjectOutputStream(fout); 88 | oos.writeObject(fileMap); 89 | } 90 | catch (IOException e) 91 | { 92 | e.printStackTrace(); 93 | } 94 | finally 95 | { 96 | try 97 | { 98 | if (fout != null) 99 | { 100 | fout.close(); 101 | } 102 | if (oos != null) 103 | { 104 | oos.close(); 105 | } 106 | } 107 | catch (IOException ioe) 108 | { 109 | ioe.printStackTrace(); 110 | } 111 | } 112 | } 113 | 114 | private HashMap loadListFromFile() 115 | { 116 | 117 | HashMap ret = new HashMap<>(); 118 | File temp = new File(thisNode.FILESPATH); 119 | if (!(temp.exists())) 120 | { 121 | temp.mkdir(); 122 | } 123 | 124 | File localFiles = new File(thisNode.FILESPATH + "index"); 125 | if (localFiles.exists()) 126 | { 127 | FileInputStream fis = null; 128 | try 129 | { 130 | fis = new FileInputStream(thisNode.FILESPATH + "index"); 131 | ObjectInputStream ois = new ObjectInputStream(fis); 132 | while (true) 133 | { 134 | ret = ((HashMap) ois.readObject()); 135 | } 136 | } 137 | catch (EOFException | FileNotFoundException | ClassNotFoundException e) 138 | { 139 | //Aspettate o impossibili 140 | } 141 | catch (IOException ioe) 142 | { 143 | ioe.printStackTrace(); 144 | } 145 | finally 146 | { 147 | try 148 | { 149 | if (fis != null) 150 | { 151 | fis.close(); 152 | } 153 | } 154 | catch (IOException ioe) 155 | { 156 | } //Ignorata 157 | finally 158 | { 159 | return ret; 160 | } 161 | } 162 | } 163 | 164 | try 165 | { 166 | localFiles.createNewFile(); 167 | } 168 | catch (IOException ioe) 169 | { 170 | ioe.printStackTrace(); 171 | } 172 | finally 173 | { 174 | return ret; 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/KPack/Tree/Bucket.java: -------------------------------------------------------------------------------- 1 | package KPack.Tree; 2 | 3 | import KPack.KadNode; 4 | import KPack.Kademlia; 5 | import static java.lang.Thread.sleep; 6 | 7 | import java.util.Iterator; 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import java.util.concurrent.atomic.AtomicBoolean; 11 | import java.util.concurrent.atomic.AtomicLong; 12 | 13 | public class Bucket extends Node implements Iterable { 14 | 15 | //SOLO foglia 16 | private int dimensioneMax; 17 | private List listaNodi; 18 | private boolean splittable; 19 | private Kademlia thisKadNode; 20 | //private long timeVisited; 21 | 22 | private AtomicLong lastUse; 23 | private AtomicBoolean isActive; 24 | private Thread refresher; 25 | 26 | public Bucket(Kademlia thisKadNode, boolean splittable) 27 | { 28 | listaNodi = new LinkedList(); 29 | dimensioneMax = thisKadNode.K; 30 | this.splittable = splittable; 31 | this.thisKadNode = thisKadNode; 32 | 33 | lastUse = new AtomicLong(System.currentTimeMillis()); 34 | isActive = new AtomicBoolean(true); 35 | refresher = null; 36 | } 37 | 38 | public synchronized boolean add(KadNode kn) 39 | { 40 | lastUse.getAndSet(System.currentTimeMillis()); 41 | listaNodi.remove(kn); 42 | if (listaNodi.size() == dimensioneMax) 43 | { 44 | if (splittable) 45 | { 46 | return false; //ci pensa l'albero 47 | } 48 | else 49 | { 50 | //pingu 1 solo nodo (il più vecchio) 51 | KadNode node = listaNodi.get(0); 52 | if (node.equals(this.thisKadNode.getMyNode())) 53 | { 54 | node = listaNodi.get(1); 55 | } 56 | if (!thisKadNode.ping(node)) 57 | { 58 | listaNodi.remove(node); 59 | listaNodi.add(kn); 60 | return true; 61 | } 62 | //Se non l'ho sostituito, scarto il nuovo nodo 63 | } 64 | } 65 | else 66 | { 67 | //C'è spazio, rimuoviamo il nodo (nel caso sia già presente) e lo riaggiungiamo 68 | //il nodo nuovo è in coda 69 | listaNodi.add(kn); 70 | } 71 | return true; //l'albero non deve gestire niente 72 | } 73 | 74 | public synchronized void removeFromBucket(KadNode kn) 75 | { 76 | listaNodi.remove(kn); 77 | } 78 | 79 | public synchronized int size() 80 | { 81 | return listaNodi.size(); 82 | } 83 | 84 | public synchronized KadNode get(int i) 85 | { 86 | return listaNodi.get(i); 87 | } 88 | 89 | public void setSplittable(boolean splittable) 90 | { 91 | this.splittable = splittable; 92 | } 93 | 94 | public boolean isSplittable() 95 | { 96 | return splittable; 97 | } 98 | 99 | public synchronized Iterator iterator() 100 | { 101 | return listaNodi.iterator(); 102 | } 103 | 104 | /* public synchronized void setTimeVisited(long now) 105 | { 106 | timeVisited = now; 107 | }*/ 108 | /* public synchronized long getTimeVisited() 109 | { 110 | return timeVisited; 111 | }*/ 112 | public synchronized String toString() 113 | { 114 | String bu = "{\n"; 115 | for (KadNode k : listaNodi) 116 | { 117 | bu += k.toString() + "\n"; 118 | } 119 | bu += "}"; 120 | return bu; 121 | } 122 | 123 | public void refreshStop() 124 | { 125 | isActive.set(false); 126 | } 127 | 128 | public void refreshStart() 129 | { 130 | if (refresher == null) 131 | { 132 | refresher = new Thread(new BucketRefresher()); 133 | refresher.start(); 134 | } 135 | } 136 | 137 | private class BucketRefresher implements Runnable { 138 | 139 | @Override 140 | public void run() 141 | { 142 | while (isActive.get()) 143 | { 144 | if (!(System.currentTimeMillis() - lastUse.get() >= thisKadNode.bucketRefreshWait)) 145 | { 146 | try 147 | { 148 | sleep(thisKadNode.bucketRefreshWait - (System.currentTimeMillis() - lastUse.get()) + 1); 149 | } 150 | catch (InterruptedException ex) 151 | { 152 | break; 153 | } 154 | } 155 | KadNode randomNode; 156 | boolean notDead; 157 | synchronized (Bucket.this) 158 | { 159 | if (size() == 0) 160 | { 161 | lastUse.set(System.currentTimeMillis()); 162 | continue; 163 | } 164 | System.out.println("Eseguo il refresh del bucket" + hashCode()); 165 | notDead = false; 166 | int randomIndex = (int) (Math.random() * size()); 167 | randomNode = get(randomIndex); //prendo un nodo random 168 | } 169 | List knowedNodes = thisKadNode.findNode(randomNode.getNodeID()); //faccio il findNode e mi restituisce una lista 170 | //devo controllare che current ci sia in lista 171 | for (KadNode kn : knowedNodes) //cerco tra i nodi se ce n'è qualcuno con il mio stesso ID 172 | { 173 | if (kn.getNodeID().equals(randomNode.getNodeID())) //ho trovato un nodo con il mio stesso ID 174 | { 175 | notDead = true; 176 | } 177 | } 178 | //se nodo non torna sicuramente è morto (lo elimino), altrimenti non posso dire nulla 179 | if (!notDead) 180 | { 181 | removeFromBucket(randomNode); 182 | } 183 | /*boolean isAlive=thisKadNode.ping(randomNode); 184 | removeFromBucket(randomNode); 185 | if (isAlive) //lo aggiungo in coda ad indicare che l'ho visto vivo di recente 186 | { 187 | listaNodi.add(randomNode); 188 | }*/ 189 | System.out.println("Refresh del bucket" + hashCode() + " completato"); 190 | lastUse.set(System.currentTimeMillis()); 191 | } 192 | System.out.println("Bucket Refresher Thread Morto"); 193 | } 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/KPack/Tree/RoutingTree.java: -------------------------------------------------------------------------------- 1 | package KPack.Tree; 2 | 3 | import KPack.KadNode; 4 | import KPack.Kademlia; 5 | import KPack.UserInterface.TreeUI; 6 | import java.awt.HeadlessException; 7 | import java.util.concurrent.locks.Lock; 8 | import java.util.concurrent.locks.ReadWriteLock; 9 | import java.util.concurrent.locks.ReentrantReadWriteLock; 10 | import java.math.BigInteger; 11 | 12 | public class RoutingTree { 13 | 14 | private Node root; 15 | private static boolean instance = false; 16 | private Kademlia thisNode = null; 17 | private int bucketsDim; 18 | private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock(); 19 | private final Lock readLock = readWriteLock.readLock(); 20 | private final Lock writeLock = readWriteLock.writeLock(); 21 | 22 | public RoutingTree(Kademlia thisNode) 23 | { 24 | if (instance) 25 | { 26 | return; 27 | } 28 | instance = true; 29 | bucketsDim = Kademlia.K; 30 | if (bucketsDim <= 0) 31 | { 32 | throw new java.lang.IllegalArgumentException("La dimensione deve essere maggiore di 0"); 33 | } 34 | root = new Bucket(thisNode, true); 35 | this.thisNode = thisNode; 36 | } 37 | 38 | public void add(KadNode nodo) 39 | { 40 | //System.out.println("Prendo il lock dell'add dell'albero"); 41 | Bucket toSplitBucket = findNodesBucket(nodo); 42 | if (!toSplitBucket.isSplittable()) 43 | { 44 | toSplitBucket.add(nodo); 45 | return; 46 | } 47 | 48 | if (!writeLock.tryLock()) 49 | { 50 | add(nodo); 51 | } 52 | else 53 | { 54 | //writeLock.lock(); 55 | while (!((toSplitBucket = findNodesBucket(nodo)).add(nodo))) 56 | { 57 | //toSplitBucket.setTimeVisited(now); 58 | toSplitBucket.refreshStop(); 59 | 60 | TreeNode temp = new TreeNode(); 61 | Bucket bucketSx = new Bucket(thisNode, false); 62 | Bucket bucketDx = new Bucket(thisNode, false); 63 | 64 | temp.setLeft(bucketSx); 65 | temp.setRight(bucketDx); 66 | bucketSx.setParent(temp); 67 | bucketDx.setParent(temp); 68 | 69 | int tempDepth = toSplitBucket.getDepth(); 70 | synchronized (toSplitBucket) 71 | { 72 | for (int i = 0; i < toSplitBucket.size(); i++) 73 | { 74 | BigInteger tempNodeID = toSplitBucket.get(i).getNodeID(); 75 | if (tempNodeID.testBit(Kademlia.BITID - tempDepth - 1)) 76 | { 77 | bucketSx.add(toSplitBucket.get(i)); 78 | } 79 | else 80 | { 81 | bucketDx.add(toSplitBucket.get(i)); 82 | } 83 | } 84 | Node tempBuckParent = toSplitBucket.getParent(); 85 | temp.setParent(tempBuckParent); 86 | //SOLO ORA vado a modificare il nodo che dev'essere splittato 87 | if (tempBuckParent == null) //il genitore di toSplitBucket è null, quindi toSplitBucket è la radice 88 | { 89 | root = temp; 90 | } 91 | else 92 | { 93 | TreeNode tempBuckParentCast = (TreeNode) tempBuckParent; 94 | //Ora il nodo originale che devo sostituire 95 | //temp diventa nodo destro o sinistro di tempBuckParent? 96 | if (tempBuckParentCast.getLeft().equals(toSplitBucket)) 97 | { 98 | tempBuckParentCast.setLeft(temp); 99 | } 100 | else 101 | { 102 | tempBuckParentCast.setRight(temp); 103 | } 104 | } 105 | 106 | //aggiorno il flag splittable 107 | if (thisNode.getNodeID().testBit(Kademlia.BITID - tempDepth - 1)) 108 | { 109 | bucketSx.setSplittable(true); 110 | } 111 | else 112 | { 113 | bucketDx.setSplittable(true); 114 | } 115 | bucketSx.refreshStart(); 116 | bucketDx.refreshStart(); 117 | } 118 | } 119 | //System.out.println("Rilascio il lock dell'add dell'albero"); 120 | writeLock.unlock(); 121 | } 122 | } 123 | 124 | public Bucket findNodesBucket(KadNode node) 125 | { 126 | //System.out.println("Prendo il lock del findNodesBucket dell'albero"); 127 | readLock.lock(); 128 | //preghiamo gli dei del java e li ringraziamo per la loro benevolenza per la classe BigInteger 129 | Node curNode = root; 130 | int i = Kademlia.BITID - 1; 131 | 132 | while (!(curNode instanceof Bucket)) 133 | { 134 | if (node.getNodeID().testBit(i--)) 135 | { 136 | curNode = ((TreeNode) curNode).getLeft(); 137 | } 138 | else 139 | { 140 | curNode = ((TreeNode) curNode).getRight(); 141 | } 142 | } 143 | //System.out.println("Rilascio il lock del findNodesBucket dell'albero"); 144 | readLock.unlock(); 145 | return (Bucket) curNode; 146 | } 147 | 148 | public Node getRoot() 149 | { 150 | readLock.lock(); 151 | try 152 | { 153 | return root; 154 | } 155 | finally 156 | { 157 | readLock.unlock(); 158 | } 159 | 160 | } 161 | 162 | /* private class RefreshThread implements Runnable 163 | { 164 | @Override 165 | public void run() 166 | { 167 | long totTime = 0; 168 | while(true) 169 | { 170 | Node curNode = root; 171 | try { 172 | if (5*60*1000-totTime <= 0) 173 | { 174 | throw new java.lang.IllegalArgumentException("Attenzione: tempo per refreshBucket maggiore di 5 minuti!!"); 175 | } 176 | Thread.sleep(5*60*1000-totTime); 177 | long timeStart = System.currentTimeMillis(); 178 | refreshBucket(curNode); 179 | long timeEnd = System.currentTimeMillis(); 180 | totTime = timeEnd - timeStart; 181 | } 182 | catch (InterruptedException ie) 183 | { 184 | System.err.println("Il thread di refresh dei bucket è stato interrotto"); 185 | } 186 | } 187 | }*/ 188 | 189 | /*private void refreshBucket(Node node) 190 | { 191 | if (node instanceof Bucket) 192 | { 193 | Bucket nodeBucket = (Bucket)node; 194 | int sizeBucket = nodeBucket.size(); 195 | boolean notDead = false; 196 | synchronized (nodeBucket) 197 | { 198 | /*Iterator nodeIterator= nodeBucket.iterator(); 199 | while(nodeIterator.hasNext()) 200 | { 201 | KadNode current = nodeIterator.next(); 202 | if(!thisNode.ping(current)) //nodo è morto 203 | { 204 | nodeBucket.removeFromBucket(current); 205 | } 206 | else 207 | { } 208 | } 209 | */ 210 | /*if(nodeBucket.getTimeVisited()<= 5*60*1000) 211 | { 212 | return; 213 | } 214 | else 215 | { 216 | int randomIndex = (int) (Math.random() * sizeBucket); 217 | KadNode randomNode = nodeBucket.get(randomIndex); //prendo un nodo random 218 | List knowedNodes = thisNode.findNode(randomNode.getNodeID()); //faccio il findNode e mi restituisce una lista 219 | //devo controllare che current ci sia in lista 220 | for (KadNode kn : knowedNodes) //cerco tra i nodi se ce n'è qualcuno con il mio stesso ID 221 | { 222 | if (kn.getNodeID().equals(randomNode.getNodeID())) //ho trovato un nodo con il mio stesso ID 223 | { 224 | notDead = true; 225 | } 226 | } 227 | //se nodo non torna sicuramente è morto (lo elimino), altrimenti non posso dire nulla 228 | if (!notDead) { 229 | nodeBucket.removeFromBucket(randomNode); 230 | } 231 | } 232 | } 233 | 234 | } 235 | else 236 | { 237 | TreeNode intNode = (TreeNode)node; 238 | refreshBucket(intNode.getLeft()); 239 | refreshBucket(intNode.getRight()); 240 | } 241 | }*/ 242 | // } 243 | } 244 | -------------------------------------------------------------------------------- /src/Main.java: -------------------------------------------------------------------------------- 1 | 2 | import KPack.Exceptions.AlreadyInstancedException; 3 | import KPack.Exceptions.FileNotKnownException; 4 | import KPack.Files.KadFile; 5 | import KPack.KadNode; 6 | import KPack.Kademlia; 7 | import java.io.File; 8 | 9 | import java.io.IOException; 10 | import java.math.BigInteger; 11 | import java.nio.file.Files; 12 | import java.security.InvalidParameterException; 13 | import java.util.List; 14 | import java.util.Scanner; 15 | 16 | public class Main { 17 | 18 | public static void main(String[] args) 19 | { 20 | Kademlia myNode = null; 21 | try 22 | { 23 | myNode = new Kademlia(); 24 | } 25 | catch (AlreadyInstancedException aie) 26 | { 27 | System.exit(1); 28 | } 29 | boolean keep = true; 30 | String in = null; 31 | Scanner reader = new Scanner(System.in); 32 | 33 | System.out.println("I file conosciuti nella lista sono: "); 34 | for (KadFile i : myNode.getFileList()) 35 | { 36 | System.out.println(i.toString()); 37 | } 38 | 39 | while (keep) 40 | { 41 | try 42 | { 43 | System.out.print("KadNode@ " + myNode.getNodeID() + "-: "); 44 | in = reader.nextLine(); 45 | String[] split = in.split(" "); 46 | switch (split[0]) 47 | { 48 | case "get": 49 | //Cerca di ottenere un file di cui conosce l'esistenza, se è possibile 50 | if (split.length > 1) 51 | { 52 | try 53 | { 54 | final Kademlia myNodeCopy=myNode; 55 | BigInteger id = new BigInteger(split[1]); 56 | new Thread(() -> 57 | { 58 | KadFile kf=null; 59 | for(KadFile fil:myNodeCopy.getFileList()) 60 | { 61 | if(fil.getFileID().equals(id)) 62 | { 63 | kf=fil; 64 | break; 65 | } 66 | } 67 | if (kf != null) 68 | { 69 | Object result = myNodeCopy.findValue(id, true); 70 | if (result instanceof byte[]) 71 | { 72 | try 73 | { 74 | String path = "." + File.separator + "myDownloadedFile" + File.separator; 75 | File directory = new File(path); 76 | if (!directory.exists()) 77 | { 78 | directory.mkdir(); 79 | } 80 | Files.write(new File(path + kf.getFileName()).toPath(), (byte[]) result); 81 | } 82 | catch (IOException ex) 83 | { 84 | } 85 | } 86 | } 87 | }).start(); 88 | } 89 | catch (Exception e) 90 | { 91 | System.out.println("ID non valido"); 92 | } 93 | } 94 | else 95 | { 96 | System.out.println("Please define a file to get"); 97 | } 98 | break; 99 | case "ls": 100 | //Lista tutti i file di cui conosco l'esistenza 101 | for (KadFile i : myNode.getFileList()) 102 | { 103 | System.out.println(i.toString()); 104 | } 105 | break; 106 | case "ip": 107 | System.out.println(myNode.getIP().getHostAddress()); 108 | break; 109 | case "store": 110 | if (split.length > 1) 111 | { 112 | try 113 | { 114 | myNode.store(split[1]); 115 | } 116 | catch (IOException | InvalidParameterException ioe) 117 | { 118 | System.out.println("File specificato non valido"); 119 | } 120 | } 121 | else 122 | { 123 | System.out.println("Inserisci il percorso del file"); 124 | } 125 | break; 126 | case "findnode": 127 | if (split.length > 1) 128 | { 129 | List l = myNode.findNode(new BigInteger(split[1])); 130 | for (int i = 0; i < l.size(); i++) 131 | { 132 | System.out.println("******* " + l.get(i)); 133 | } 134 | } 135 | else 136 | { 137 | System.out.println("Inserisci l'ID del nodo"); 138 | } 139 | break; 140 | case "delete": 141 | if (split.length > 1) 142 | { 143 | try 144 | { 145 | myNode.delete(new BigInteger(split[1])); 146 | } 147 | catch (FileNotKnownException fnk) 148 | { 149 | System.out.println("Il file non esiste o non ne sei il proprietario"); 150 | } 151 | } 152 | else 153 | { 154 | System.out.println("Inserisci L'ID del file"); 155 | } 156 | break; 157 | case "ping": 158 | //Funzione di test 159 | if (split.length > 1) 160 | { 161 | try 162 | { 163 | if (myNode.ping(new KadNode(split[1], Short.parseShort(split[2]), new BigInteger(split[3])))) 164 | { 165 | System.out.println("VIVO"); 166 | } 167 | } 168 | catch (NumberFormatException nfe) 169 | { 170 | if (myNode.ping(new KadNode(split[1], myNode.getPort(), new BigInteger(split[2])))) 171 | { 172 | System.out.println("VIVO"); 173 | } 174 | } 175 | } 176 | else 177 | { 178 | System.out.println("Please define an IP address and ID or IP,Port and ID"); 179 | } 180 | break; 181 | case "findvalue": 182 | if (split.length > 1) 183 | { 184 | Object value = myNode.findValue(new BigInteger(split[1]), false); 185 | if (value instanceof List) 186 | { 187 | for (int i = 0; i < ((List) value).size(); i++) 188 | { 189 | System.out.println("******* " + ((List) value).get(i)); 190 | } 191 | } 192 | else 193 | { 194 | System.out.println("Il contenuto del file richiesto è stato trovato"); 195 | } 196 | 197 | } 198 | else 199 | { 200 | System.out.println("Inserisci l'ID del nodo"); 201 | } 202 | break; 203 | case "printtree": 204 | System.out.println(myNode.printTree()); 205 | break; 206 | case "exit": 207 | System.exit(0); 208 | break; 209 | case "help": 210 | System.out.println("I possibili comandi da inserire sono i seguenti:"); 211 | System.out.println("get: per ottenere uno specifico file"); 212 | System.out.println("ls: per ottenere la lista di file di cui si è proprietari presenti nella rete"); 213 | System.out.println("store: per memorizzare un file nella rete"); 214 | System.out.println("delete: per eliminare un file dalla rete"); 215 | System.out.println("ip: per conoscere il proprio ip"); 216 | System.out.println("ping: per controllare che un nodo sia attivo o meno nella rete"); 217 | System.out.println("findvalue: per ottenere il contenuto del file"); 218 | System.out.println("findnode: per restituire una lista contenente fino a K nodi della rete più vicini all’ID specificato."); 219 | System.out.println("printtree: per visualizzare l'albero di routing"); 220 | System.out.println("exit: per uscire"); 221 | break; 222 | default: 223 | System.out.println("Say what? Digitare \"help\" per visualizzare lista di comandi"); 224 | break; 225 | } 226 | } 227 | 228 | catch (ArrayIndexOutOfBoundsException aioobe) 229 | { 230 | System.out.println("Parametri non validi"); 231 | } 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /src/KPack/UserInterface/UserInterface.java: -------------------------------------------------------------------------------- 1 | package KPack.UserInterface; 2 | 3 | import KPack.Exceptions.FileNotKnownException; 4 | import KPack.Files.KadFile; 5 | import KPack.Files.KadFileMap; 6 | import KPack.KadNode; 7 | import KPack.Kademlia; 8 | import java.io.File; 9 | import java.io.FileNotFoundException; 10 | import java.io.IOException; 11 | import static java.lang.Thread.sleep; 12 | import java.math.BigInteger; 13 | import java.nio.file.Files; 14 | import java.security.InvalidParameterException; 15 | import java.util.List; 16 | import java.util.logging.Level; 17 | import java.util.logging.Logger; 18 | import javax.swing.DefaultListModel; 19 | import javax.swing.JFileChooser; 20 | 21 | public class UserInterface extends javax.swing.JFrame { 22 | 23 | private Kademlia node; 24 | private KadFileMap fileMap; 25 | private KadFileMap localFileMap; 26 | 27 | DefaultListModel myFileModel = new DefaultListModel<>(); 28 | DefaultListModel localFileModel = new DefaultListModel<>(); 29 | 30 | public UserInterface(Kademlia node, KadFileMap fileMap, KadFileMap localFileMap) 31 | { 32 | this.node = node; 33 | this.fileMap = fileMap; 34 | this.localFileMap = localFileMap; 35 | 36 | initComponents(); 37 | 38 | myFile.setModel(localFileModel); 39 | redundantFile.setModel(myFileModel); 40 | 41 | KadNode thisKadNode = node.getMyNode(); 42 | UIID.setText(thisKadNode.getNodeID().intValue() + ""); 43 | UIIP.setText(thisKadNode.getIp().getHostAddress()); 44 | UIport.setText(thisKadNode.getPort() + ""); 45 | 46 | localFileMap.forEach((t, u) -> 47 | { 48 | localFileModel.addElement(t.intValue() + " - " + u.getFileName()); 49 | }); 50 | 51 | new Thread(new UIRefresher()).start(); 52 | 53 | this.setVisible(true); 54 | } 55 | 56 | // //GEN-BEGIN:initComponents 57 | private void initComponents() 58 | { 59 | 60 | jPanel1 = new javax.swing.JPanel(); 61 | jLabel1 = new javax.swing.JLabel(); 62 | selectedFile = new javax.swing.JTextField(); 63 | jButton2 = new javax.swing.JButton(); 64 | jButton3 = new javax.swing.JButton(); 65 | jPanel2 = new javax.swing.JPanel(); 66 | jScrollPane1 = new javax.swing.JScrollPane(); 67 | myFile = new javax.swing.JList<>(); 68 | jLabel2 = new javax.swing.JLabel(); 69 | jButton1 = new javax.swing.JButton(); 70 | jPanel3 = new javax.swing.JPanel(); 71 | jLabel3 = new javax.swing.JLabel(); 72 | jScrollPane2 = new javax.swing.JScrollPane(); 73 | redundantFile = new javax.swing.JList<>(); 74 | jPanel4 = new javax.swing.JPanel(); 75 | jLabel4 = new javax.swing.JLabel(); 76 | jLabel5 = new javax.swing.JLabel(); 77 | jLabel6 = new javax.swing.JLabel(); 78 | jLabel7 = new javax.swing.JLabel(); 79 | UIport = new javax.swing.JLabel(); 80 | UIIP = new javax.swing.JLabel(); 81 | UIID = new javax.swing.JLabel(); 82 | jPanel5 = new javax.swing.JPanel(); 83 | jLabel8 = new javax.swing.JLabel(); 84 | jButton4 = new javax.swing.JButton(); 85 | jLabel9 = new javax.swing.JLabel(); 86 | findNodeID = new javax.swing.JTextField(); 87 | jScrollPane3 = new javax.swing.JScrollPane(); 88 | findNodeResult = new javax.swing.JTextArea(); 89 | jPanel6 = new javax.swing.JPanel(); 90 | jLabel10 = new javax.swing.JLabel(); 91 | fileID = new javax.swing.JTextField(); 92 | jLabel11 = new javax.swing.JLabel(); 93 | jButton5 = new javax.swing.JButton(); 94 | filePosition = new javax.swing.JLabel(); 95 | 96 | setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE); 97 | setTitle("Kademlia"); 98 | setResizable(false); 99 | 100 | jPanel1.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED)); 101 | 102 | jLabel1.setText("File Store"); 103 | 104 | selectedFile.setEditable(false); 105 | 106 | jButton2.setText("Store"); 107 | jButton2.addActionListener(new java.awt.event.ActionListener() 108 | { 109 | public void actionPerformed(java.awt.event.ActionEvent evt) 110 | { 111 | jButton2ActionPerformed(evt); 112 | } 113 | }); 114 | 115 | jButton3.setText("Select"); 116 | jButton3.addActionListener(new java.awt.event.ActionListener() 117 | { 118 | public void actionPerformed(java.awt.event.ActionEvent evt) 119 | { 120 | jButton3ActionPerformed(evt); 121 | } 122 | }); 123 | 124 | javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); 125 | jPanel1.setLayout(jPanel1Layout); 126 | jPanel1Layout.setHorizontalGroup( 127 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 128 | .addGroup(jPanel1Layout.createSequentialGroup() 129 | .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 130 | .addGroup(jPanel1Layout.createSequentialGroup() 131 | .addGap(250, 250, 250) 132 | .addComponent(jLabel1)) 133 | .addGroup(jPanel1Layout.createSequentialGroup() 134 | .addContainerGap() 135 | .addComponent(selectedFile, javax.swing.GroupLayout.PREFERRED_SIZE, 406, javax.swing.GroupLayout.PREFERRED_SIZE) 136 | .addGap(37, 37, 37) 137 | .addComponent(jButton3, javax.swing.GroupLayout.PREFERRED_SIZE, 93, javax.swing.GroupLayout.PREFERRED_SIZE)) 138 | .addGroup(jPanel1Layout.createSequentialGroup() 139 | .addGap(210, 210, 210) 140 | .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 169, javax.swing.GroupLayout.PREFERRED_SIZE))) 141 | .addContainerGap(32, Short.MAX_VALUE)) 142 | ); 143 | jPanel1Layout.setVerticalGroup( 144 | jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 145 | .addGroup(jPanel1Layout.createSequentialGroup() 146 | .addContainerGap() 147 | .addComponent(jLabel1) 148 | .addGap(51, 51, 51) 149 | .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 150 | .addComponent(selectedFile, javax.swing.GroupLayout.PREFERRED_SIZE, 39, javax.swing.GroupLayout.PREFERRED_SIZE) 151 | .addComponent(jButton3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 152 | .addGap(57, 57, 57) 153 | .addComponent(jButton2, javax.swing.GroupLayout.PREFERRED_SIZE, 67, javax.swing.GroupLayout.PREFERRED_SIZE) 154 | .addContainerGap(88, Short.MAX_VALUE)) 155 | ); 156 | 157 | jPanel2.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED)); 158 | 159 | myFile.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); 160 | jScrollPane1.setViewportView(myFile); 161 | 162 | jLabel2.setText("My File"); 163 | 164 | jButton1.setText("Delete"); 165 | jButton1.addActionListener(new java.awt.event.ActionListener() 166 | { 167 | public void actionPerformed(java.awt.event.ActionEvent evt) 168 | { 169 | jButton1ActionPerformed(evt); 170 | } 171 | }); 172 | 173 | javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); 174 | jPanel2.setLayout(jPanel2Layout); 175 | jPanel2Layout.setHorizontalGroup( 176 | jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 177 | .addGroup(jPanel2Layout.createSequentialGroup() 178 | .addContainerGap() 179 | .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 333, Short.MAX_VALUE) 180 | .addContainerGap()) 181 | .addGroup(jPanel2Layout.createSequentialGroup() 182 | .addGap(158, 158, 158) 183 | .addComponent(jLabel2) 184 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 185 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup() 186 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 187 | .addComponent(jButton1) 188 | .addGap(138, 138, 138)) 189 | ); 190 | jPanel2Layout.setVerticalGroup( 191 | jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 192 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel2Layout.createSequentialGroup() 193 | .addContainerGap() 194 | .addComponent(jLabel2) 195 | .addGap(18, 18, 18) 196 | .addComponent(jScrollPane1) 197 | .addGap(18, 18, 18) 198 | .addComponent(jButton1) 199 | .addGap(24, 24, 24)) 200 | ); 201 | 202 | jPanel3.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED)); 203 | 204 | jLabel3.setText("Redundant File"); 205 | 206 | redundantFile.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); 207 | jScrollPane2.setViewportView(redundantFile); 208 | 209 | javax.swing.GroupLayout jPanel3Layout = new javax.swing.GroupLayout(jPanel3); 210 | jPanel3.setLayout(jPanel3Layout); 211 | jPanel3Layout.setHorizontalGroup( 212 | jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 213 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel3Layout.createSequentialGroup() 214 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 215 | .addComponent(jLabel3) 216 | .addGap(112, 112, 112)) 217 | .addGroup(jPanel3Layout.createSequentialGroup() 218 | .addContainerGap() 219 | .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 322, Short.MAX_VALUE) 220 | .addContainerGap()) 221 | ); 222 | jPanel3Layout.setVerticalGroup( 223 | jPanel3Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 224 | .addGroup(jPanel3Layout.createSequentialGroup() 225 | .addContainerGap() 226 | .addComponent(jLabel3) 227 | .addGap(18, 18, 18) 228 | .addComponent(jScrollPane2) 229 | .addContainerGap()) 230 | ); 231 | 232 | jPanel4.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED)); 233 | 234 | jLabel4.setText("Node Info"); 235 | 236 | jLabel5.setText("ID:"); 237 | 238 | jLabel6.setText("IP:"); 239 | 240 | jLabel7.setText("Port:"); 241 | 242 | UIport.setText("vvvvvvvvvvvvvvvvvvv"); 243 | UIport.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT); 244 | 245 | UIIP.setText("rffff"); 246 | UIIP.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT); 247 | 248 | UIID.setText("vvvv"); 249 | UIID.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT); 250 | 251 | javax.swing.GroupLayout jPanel4Layout = new javax.swing.GroupLayout(jPanel4); 252 | jPanel4.setLayout(jPanel4Layout); 253 | jPanel4Layout.setHorizontalGroup( 254 | jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 255 | .addGroup(jPanel4Layout.createSequentialGroup() 256 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 257 | .addGroup(jPanel4Layout.createSequentialGroup() 258 | .addGap(256, 256, 256) 259 | .addComponent(jLabel4)) 260 | .addGroup(jPanel4Layout.createSequentialGroup() 261 | .addGap(36, 36, 36) 262 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 263 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) 264 | .addComponent(jLabel6, javax.swing.GroupLayout.Alignment.LEADING) 265 | .addComponent(jLabel5)) 266 | .addComponent(jLabel7)) 267 | .addGap(65, 65, 65) 268 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) 269 | .addComponent(UIIP, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 270 | .addComponent(UIport, javax.swing.GroupLayout.DEFAULT_SIZE, 122, Short.MAX_VALUE) 271 | .addComponent(UIID, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))) 272 | .addGap(0, 269, Short.MAX_VALUE)) 273 | ); 274 | jPanel4Layout.setVerticalGroup( 275 | jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 276 | .addGroup(jPanel4Layout.createSequentialGroup() 277 | .addContainerGap() 278 | .addComponent(jLabel4) 279 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 280 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 281 | .addComponent(jLabel5) 282 | .addComponent(UIID)) 283 | .addGap(18, 18, 18) 284 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 285 | .addComponent(jLabel6) 286 | .addComponent(UIIP)) 287 | .addGap(18, 18, 18) 288 | .addGroup(jPanel4Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 289 | .addComponent(jLabel7) 290 | .addComponent(UIport)) 291 | .addContainerGap(34, Short.MAX_VALUE)) 292 | ); 293 | 294 | jPanel5.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED)); 295 | 296 | jLabel8.setText("Find Node"); 297 | 298 | jButton4.setText("Find"); 299 | jButton4.addActionListener(new java.awt.event.ActionListener() 300 | { 301 | public void actionPerformed(java.awt.event.ActionEvent evt) 302 | { 303 | jButton4ActionPerformed(evt); 304 | } 305 | }); 306 | 307 | jLabel9.setText("ID:"); 308 | 309 | findNodeResult.setEditable(false); 310 | findNodeResult.setColumns(20); 311 | findNodeResult.setRows(5); 312 | jScrollPane3.setViewportView(findNodeResult); 313 | 314 | javax.swing.GroupLayout jPanel5Layout = new javax.swing.GroupLayout(jPanel5); 315 | jPanel5.setLayout(jPanel5Layout); 316 | jPanel5Layout.setHorizontalGroup( 317 | jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 318 | .addGroup(jPanel5Layout.createSequentialGroup() 319 | .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) 320 | .addGroup(jPanel5Layout.createSequentialGroup() 321 | .addGap(164, 164, 164) 322 | .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) 323 | .addComponent(jButton4) 324 | .addComponent(jLabel8))) 325 | .addGroup(jPanel5Layout.createSequentialGroup() 326 | .addGap(101, 101, 101) 327 | .addComponent(jLabel9) 328 | .addGap(18, 18, 18) 329 | .addComponent(findNodeID))) 330 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 331 | .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 322, javax.swing.GroupLayout.PREFERRED_SIZE) 332 | .addContainerGap()) 333 | ); 334 | jPanel5Layout.setVerticalGroup( 335 | jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 336 | .addGroup(jPanel5Layout.createSequentialGroup() 337 | .addComponent(jLabel8) 338 | .addGap(35, 35, 35) 339 | .addGroup(jPanel5Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 340 | .addComponent(jLabel9) 341 | .addComponent(findNodeID, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) 342 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 37, Short.MAX_VALUE) 343 | .addComponent(jButton4) 344 | .addGap(24, 24, 24)) 345 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel5Layout.createSequentialGroup() 346 | .addGap(0, 0, Short.MAX_VALUE) 347 | .addComponent(jScrollPane3, javax.swing.GroupLayout.PREFERRED_SIZE, 143, javax.swing.GroupLayout.PREFERRED_SIZE) 348 | .addContainerGap()) 349 | ); 350 | 351 | jPanel6.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED)); 352 | 353 | jLabel10.setText("Get File"); 354 | 355 | jLabel11.setText("ID:"); 356 | 357 | jButton5.setText("Get"); 358 | jButton5.addActionListener(new java.awt.event.ActionListener() 359 | { 360 | public void actionPerformed(java.awt.event.ActionEvent evt) 361 | { 362 | jButton5ActionPerformed(evt); 363 | } 364 | }); 365 | 366 | javax.swing.GroupLayout jPanel6Layout = new javax.swing.GroupLayout(jPanel6); 367 | jPanel6.setLayout(jPanel6Layout); 368 | jPanel6Layout.setHorizontalGroup( 369 | jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 370 | .addGroup(jPanel6Layout.createSequentialGroup() 371 | .addGap(211, 211, 211) 372 | .addComponent(jLabel11) 373 | .addGap(18, 18, 18) 374 | .addGroup(jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 375 | .addComponent(jButton5) 376 | .addComponent(fileID, javax.swing.GroupLayout.PREFERRED_SIZE, 74, javax.swing.GroupLayout.PREFERRED_SIZE) 377 | .addComponent(jLabel10)) 378 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 379 | .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel6Layout.createSequentialGroup() 380 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 381 | .addComponent(filePosition, javax.swing.GroupLayout.PREFERRED_SIZE, 371, javax.swing.GroupLayout.PREFERRED_SIZE) 382 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 383 | ); 384 | jPanel6Layout.setVerticalGroup( 385 | jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 386 | .addGroup(jPanel6Layout.createSequentialGroup() 387 | .addComponent(jLabel10) 388 | .addGap(38, 38, 38) 389 | .addGroup(jPanel6Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) 390 | .addComponent(fileID, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 391 | .addComponent(jLabel11)) 392 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 393 | .addComponent(jButton5) 394 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) 395 | .addComponent(filePosition) 396 | .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 397 | ); 398 | 399 | javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); 400 | getContentPane().setLayout(layout); 401 | layout.setHorizontalGroup( 402 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 403 | .addGroup(layout.createSequentialGroup() 404 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 405 | .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 406 | .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 407 | .addComponent(jPanel6, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 408 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 409 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 410 | .addGroup(layout.createSequentialGroup() 411 | .addComponent(jPanel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) 412 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 413 | .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 414 | .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) 415 | ); 416 | layout.setVerticalGroup( 417 | layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 418 | .addGroup(layout.createSequentialGroup() 419 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) 420 | .addComponent(jPanel3, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 421 | .addComponent(jPanel2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 422 | .addGroup(layout.createSequentialGroup() 423 | .addComponent(jPanel4, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 424 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 425 | .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) 426 | .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) 427 | .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 428 | .addComponent(jPanel5, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) 429 | .addComponent(jPanel6, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 430 | .addContainerGap()) 431 | ); 432 | 433 | pack(); 434 | }// //GEN-END:initComponents 435 | 436 | private void jButton3ActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jButton3ActionPerformed 437 | {//GEN-HEADEREND:event_jButton3ActionPerformed 438 | JFileChooser fileChooser = new JFileChooser(); 439 | int returnVal = fileChooser.showOpenDialog(this); 440 | if (returnVal == JFileChooser.APPROVE_OPTION) 441 | { 442 | File file = fileChooser.getSelectedFile(); 443 | selectedFile.setText(file.getAbsolutePath()); 444 | } 445 | }//GEN-LAST:event_jButton3ActionPerformed 446 | 447 | private void jButton2ActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jButton2ActionPerformed 448 | {//GEN-HEADEREND:event_jButton2ActionPerformed 449 | setButtonEnable(false); 450 | if (!selectedFile.getText().equals("")) 451 | { 452 | try 453 | { 454 | node.store(selectedFile.getText()); 455 | selectedFile.setText(""); 456 | localFileModel.clear(); 457 | localFileMap.forEach((t, u) -> 458 | { 459 | localFileModel.addElement(t.intValue() + " - " + u.getFileName()); 460 | }); 461 | } 462 | catch (FileNotFoundException | InvalidParameterException ex) 463 | { 464 | } 465 | } 466 | setButtonEnable(true); 467 | }//GEN-LAST:event_jButton2ActionPerformed 468 | 469 | private void jButton1ActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jButton1ActionPerformed 470 | {//GEN-HEADEREND:event_jButton1ActionPerformed 471 | setButtonEnable(false); 472 | myFile.setEnabled(false); 473 | int sel = myFile.getSelectedIndex(); 474 | if (sel != -1) 475 | { 476 | String el = myFile.getSelectedValue(); 477 | String idS = el.split(" ")[0]; 478 | try 479 | { 480 | node.delete(new BigInteger(idS)); 481 | localFileModel.remove(sel); 482 | } 483 | catch (FileNotKnownException ex) 484 | { 485 | } 486 | } 487 | myFile.setEnabled(true); 488 | setButtonEnable(true); 489 | }//GEN-LAST:event_jButton1ActionPerformed 490 | 491 | private void jButton4ActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jButton4ActionPerformed 492 | {//GEN-HEADEREND:event_jButton4ActionPerformed 493 | jButton4.setEnabled(false); 494 | String idString = findNodeID.getText(); 495 | findNodeResult.setText(""); 496 | try 497 | { 498 | BigInteger id = new BigInteger(idString); 499 | new Thread(() -> 500 | { 501 | List nodes = node.findNode(id); 502 | nodes.forEach((t) -> 503 | { 504 | findNodeResult.append(t + "\n"); 505 | }); 506 | jButton4.setEnabled(true); 507 | }).start(); 508 | } 509 | catch (Exception e) 510 | { 511 | findNodeResult.setText("ID non valido"); 512 | jButton4.setEnabled(true); 513 | } 514 | }//GEN-LAST:event_jButton4ActionPerformed 515 | 516 | private void jButton5ActionPerformed(java.awt.event.ActionEvent evt)//GEN-FIRST:event_jButton5ActionPerformed 517 | {//GEN-HEADEREND:event_jButton5ActionPerformed 518 | jButton5.setEnabled(false); 519 | findNodeResult.setText(""); 520 | String idFile = fileID.getText(); 521 | try 522 | { 523 | BigInteger id = new BigInteger(idFile); 524 | 525 | new Thread(() -> 526 | { 527 | 528 | KadFile kf = localFileMap.get(id); 529 | if (kf != null) 530 | { 531 | Object result = node.findValue(id, true); 532 | if (result instanceof byte[]) 533 | { 534 | try 535 | { 536 | String path="." + File.separator + "myDownloadedFile" + File.separator; 537 | File directory = new File(path); 538 | if(!directory.exists()) 539 | directory.mkdir(); 540 | Files.write(new File(path+kf.getFileName()).toPath(), (byte[]) result); 541 | } 542 | catch (IOException ex) 543 | { 544 | } 545 | } 546 | } 547 | jButton5.setEnabled(true); 548 | }).start(); 549 | } 550 | catch (Exception e) 551 | { 552 | findNodeResult.setText("ID non valido"); 553 | jButton5.setEnabled(true); 554 | } 555 | }//GEN-LAST:event_jButton5ActionPerformed 556 | 557 | private void setButtonEnable(boolean val) 558 | { 559 | jButton2.setEnabled(val); 560 | jButton3.setEnabled(val); 561 | jButton1.setEnabled(val); 562 | jButton4.setEnabled(val); 563 | jButton5.setEnabled(val); 564 | } 565 | 566 | private class UIRefresher implements Runnable { 567 | 568 | @Override 569 | public void run() 570 | { 571 | while (true) 572 | { 573 | try 574 | { 575 | sleep(1000); 576 | } 577 | catch (InterruptedException ex) 578 | { 579 | continue; 580 | } 581 | // int myFileSel = myFile.getSelectedIndex(); 582 | 583 | myFileModel.clear(); 584 | fileMap.forEach((t, u) -> 585 | { 586 | myFileModel.addElement(t.intValue() + " - " + u.getFileName()); 587 | }); 588 | } 589 | } 590 | 591 | } 592 | // Variables declaration - do not modify//GEN-BEGIN:variables 593 | private javax.swing.JLabel UIID; 594 | private javax.swing.JLabel UIIP; 595 | private javax.swing.JLabel UIport; 596 | private javax.swing.JTextField fileID; 597 | private javax.swing.JLabel filePosition; 598 | private javax.swing.JTextField findNodeID; 599 | private javax.swing.JTextArea findNodeResult; 600 | private javax.swing.JButton jButton1; 601 | private javax.swing.JButton jButton2; 602 | private javax.swing.JButton jButton3; 603 | private javax.swing.JButton jButton4; 604 | private javax.swing.JButton jButton5; 605 | private javax.swing.JLabel jLabel1; 606 | private javax.swing.JLabel jLabel10; 607 | private javax.swing.JLabel jLabel11; 608 | private javax.swing.JLabel jLabel2; 609 | private javax.swing.JLabel jLabel3; 610 | private javax.swing.JLabel jLabel4; 611 | private javax.swing.JLabel jLabel5; 612 | private javax.swing.JLabel jLabel6; 613 | private javax.swing.JLabel jLabel7; 614 | private javax.swing.JLabel jLabel8; 615 | private javax.swing.JLabel jLabel9; 616 | private javax.swing.JPanel jPanel1; 617 | private javax.swing.JPanel jPanel2; 618 | private javax.swing.JPanel jPanel3; 619 | private javax.swing.JPanel jPanel4; 620 | private javax.swing.JPanel jPanel5; 621 | private javax.swing.JPanel jPanel6; 622 | private javax.swing.JScrollPane jScrollPane1; 623 | private javax.swing.JScrollPane jScrollPane2; 624 | private javax.swing.JScrollPane jScrollPane3; 625 | private javax.swing.JList myFile; 626 | private javax.swing.JList redundantFile; 627 | private javax.swing.JTextField selectedFile; 628 | // End of variables declaration//GEN-END:variables 629 | } 630 | -------------------------------------------------------------------------------- /src/KPack/Kademlia.java: -------------------------------------------------------------------------------- 1 | package KPack; 2 | 3 | import KPack.Exceptions.AlreadyInstancedException; 4 | import KPack.Exceptions.FileNotKnownException; 5 | import KPack.Exceptions.InvalidSettingsException; 6 | import KPack.Files.KadFile; 7 | import KPack.Files.KadFileMap; 8 | import KPack.Packets.*; 9 | import KPack.Tree.Bucket; 10 | import KPack.Tree.Node; 11 | import KPack.Tree.RoutingTree; 12 | import KPack.Tree.TreeNode; 13 | import KPack.UserInterface.TreeUI; 14 | import KPack.UserInterface.UserInterface; 15 | 16 | import java.awt.*; 17 | import java.io.*; 18 | import static java.lang.Thread.sleep; 19 | import java.math.BigInteger; 20 | import java.net.*; 21 | import java.nio.charset.StandardCharsets; 22 | import java.nio.file.Files; 23 | import java.security.InvalidParameterException; 24 | import java.util.*; 25 | import java.util.List; 26 | import java.util.concurrent.atomic.AtomicReference; 27 | import java.util.concurrent.locks.Lock; 28 | import java.util.concurrent.locks.ReadWriteLock; 29 | import java.util.concurrent.locks.ReentrantReadWriteLock; 30 | import java.util.logging.Level; 31 | import java.util.logging.Logger; 32 | 33 | public class Kademlia implements KademliaInterf { 34 | 35 | private static boolean instance = false; 36 | public final static int BITID = 8; 37 | public final static int K = 4; 38 | public final static int ALPHA = 2; 39 | public final static String FILESPATH = "." + File.separator + "storedFiles" + File.separator; 40 | private KadFileMap fileMap; 41 | private KadFileMap localFileMap; 42 | private BigInteger nodeID; 43 | private RoutingTree routingTree; 44 | private KadNode thisNode; 45 | private ArrayList fixedNodesList = new ArrayList<>(); 46 | private Set> recentlyRefreshed; 47 | 48 | //Variabili lette dalle impostazioni 49 | public int port = 1337; 50 | public int fileRefreshWait = 100000; 51 | public int fileRefreshThreadSleep = 20000; 52 | public int bucketRefreshWait = 20000; 53 | private int timeout = 10000; 54 | 55 | private final ReadWriteLock localFileReadWriteLock; 56 | private final Lock localFileReadLock; 57 | private final Lock localFileWriteLock; 58 | 59 | private final ReadWriteLock globalFileReadWriteLock; 60 | private final Lock globalFileReadLock; 61 | private final Lock globalFileWriteLock; 62 | 63 | public Kademlia() throws AlreadyInstancedException 64 | { 65 | if (instance) 66 | { 67 | throw new AlreadyInstancedException(); 68 | } 69 | instance = true; 70 | Runtime.getRuntime().addShutdownHook(new Thread() { //Hook per eliminare i file ridondanti allo spegnimento 71 | @Override 72 | public void run() 73 | { 74 | File temp = new File(FILESPATH); 75 | if (temp.listFiles() != null) 76 | { 77 | for (File i : temp.listFiles()) 78 | { 79 | if (i.getName().endsWith(".kad")) 80 | { 81 | i.delete(); 82 | } 83 | } 84 | } 85 | } 86 | }); 87 | 88 | System.out.println("WorkingDir: " + System.getProperty("user.dir")); 89 | 90 | loadSettings(); 91 | 92 | //Lo rieseguo, potrebbe non essere stato eseguito in seguito ad un crash della JVM 93 | File temp = new File(FILESPATH); 94 | if (temp.listFiles() != null) 95 | { 96 | for (File i : temp.listFiles()) 97 | { 98 | if (i.getName().endsWith(".kad")) 99 | { 100 | i.delete(); 101 | } 102 | } 103 | } 104 | 105 | localFileReadWriteLock = new ReentrantReadWriteLock(true); 106 | localFileReadLock = localFileReadWriteLock.readLock(); 107 | localFileWriteLock = localFileReadWriteLock.writeLock(); 108 | 109 | globalFileReadWriteLock = new ReentrantReadWriteLock(true); 110 | globalFileReadLock = globalFileReadWriteLock.readLock(); 111 | globalFileWriteLock = globalFileReadWriteLock.writeLock(); 112 | 113 | fileMap = new KadFileMap(this, false); 114 | localFileMap = new KadFileMap(this, true); 115 | String myIP = getIP().getHostAddress().toString(); 116 | 117 | routingTree = new RoutingTree(this); 118 | 119 | fixedNodesList = loadFixedNodesFromFile(); 120 | recentlyRefreshed = new HashSet<>(); 121 | 122 | //Aggiungo all'alberto i nodi noti 123 | for (FixedKadNode fkn : fixedNodesList) 124 | { 125 | routingTree.add(fkn.getKadNode()); 126 | } 127 | 128 | if (!isFixedNode()) 129 | { 130 | do 131 | { 132 | nodeID = new BigInteger(BITID, new Random()); 133 | System.out.println("ID generato: " + nodeID); 134 | } 135 | while (!isUniqueID()); 136 | } 137 | System.out.println("ID ottenuto: " + nodeID); 138 | 139 | thisNode = new KadNode(myIP, port, nodeID); 140 | 141 | routingTree.add(thisNode); //Mi aggiungo 142 | 143 | try 144 | { 145 | new TreeUI(routingTree, this); 146 | new UserInterface(this, fileMap, localFileMap); 147 | } 148 | catch (HeadlessException he) //per dispositivi senza schermo 149 | { 150 | } 151 | 152 | new Thread(new ListenerThread(), "Listener").start(); 153 | 154 | networkJoin(); 155 | 156 | new Thread(new FileRefresh(fileRefreshWait, fileRefreshThreadSleep), "FileRefresh").start(); 157 | 158 | synchronized (routingTree.getRoot()) 159 | { 160 | if (routingTree.getRoot() instanceof Bucket) 161 | { 162 | ((Bucket) routingTree.getRoot()).refreshStart(); 163 | } 164 | } 165 | } 166 | 167 | private boolean isUniqueID() 168 | { 169 | List nodes = findNode(nodeID, false); //richiamo il findnode utilizzando il mio ID 170 | for (KadNode kn : nodes) //cerco tra i nodi se ce n'è qualcuno con il mio stesso ID 171 | { 172 | if (kn.getNodeID().equals(nodeID)) //ho trovato un nodo con il mio stesso ID 173 | { 174 | return false; 175 | } 176 | } 177 | return true; //non ci sono altri nodi con il mio ID, quindi è unico! 178 | } 179 | 180 | private void loadSettings() 181 | { 182 | File temp = new File("./settings.properties"); 183 | try 184 | { 185 | if (!temp.exists()) 186 | { 187 | throw new InvalidSettingsException("File di configurazione settings.properties non trovato"); 188 | } 189 | for (String i : Files.readAllLines(temp.toPath(), StandardCharsets.UTF_8)) 190 | { 191 | String sub = i.substring(0, 1); 192 | if (sub.equals("#")) 193 | { 194 | continue; 195 | } 196 | else 197 | { 198 | String[] split = i.split("="); 199 | switch (split[0]) 200 | { 201 | case "port": 202 | if (!split[1].isEmpty()) 203 | { 204 | if (Integer.valueOf(split[1]) > 1024 && Integer.valueOf(split[1]) < 65535) 205 | { 206 | port = Integer.valueOf(split[1]); 207 | } 208 | else 209 | { 210 | throw new InvalidSettingsException("Porta non valida"); 211 | } 212 | } 213 | else 214 | { 215 | throw new InvalidSettingsException("Porta non valida"); 216 | } 217 | break; 218 | case "bucketRefreshWait": 219 | if (!split[1].isEmpty()) 220 | { 221 | try 222 | { 223 | bucketRefreshWait = Integer.parseInt(split[1]); 224 | System.out.println("\u001B[31mBucketRefreshWait: " + bucketRefreshWait + "\u001B[0m"); 225 | } 226 | catch (NumberFormatException | NullPointerException e) 227 | { 228 | throw new InvalidSettingsException("Tempo di refresh dei bucket non valido"); 229 | } 230 | } 231 | else 232 | { 233 | throw new InvalidSettingsException("Tempo di refresh dei bucket non valido"); 234 | } 235 | break; 236 | case "fileRefreshWait": 237 | if (!split[1].isEmpty()) 238 | { 239 | try 240 | { 241 | fileRefreshWait = Integer.parseInt(split[1]); 242 | System.out.println("FileRefreshWait: " + fileRefreshWait); 243 | } 244 | catch (NumberFormatException | NullPointerException e) 245 | { 246 | throw new InvalidSettingsException("Tempo di refresh dei file non valido"); 247 | } 248 | } 249 | else 250 | { 251 | throw new InvalidSettingsException("Tempo di refresh dei file non valido"); 252 | } 253 | break; 254 | case "socketTimeout": 255 | if (!split[1].isEmpty()) 256 | { 257 | try 258 | { 259 | timeout = Integer.parseInt(split[1]); 260 | System.out.println("Timeout Socket: " + timeout); 261 | } 262 | catch (NumberFormatException | NullPointerException e) 263 | { 264 | throw new InvalidSettingsException("Timeout non valido"); 265 | } 266 | } 267 | else 268 | { 269 | throw new InvalidSettingsException("Timeout non valido"); 270 | } 271 | break; 272 | case "fileRefreshThreadSleep": 273 | if (!split[1].isEmpty()) 274 | { 275 | try 276 | { 277 | fileRefreshThreadSleep = Integer.parseInt(split[1]); 278 | System.out.println("fileResfreshThreadSleep: " + fileRefreshThreadSleep); 279 | } 280 | catch (NumberFormatException | NullPointerException e) 281 | { 282 | throw new InvalidSettingsException("Tempo di sleep del thread FileRefresh non valido"); 283 | } 284 | } 285 | else 286 | { 287 | throw new InvalidSettingsException("Tempo di sleep del thread FileRefresh non valido"); 288 | } 289 | break; 290 | default: 291 | throw new InvalidSettingsException("Parametro non valido: " + split[0]); 292 | } 293 | } 294 | //Check di altri vincoli 295 | if (fileRefreshThreadSleep >= fileRefreshWait) 296 | { 297 | throw new InvalidSettingsException("Il tempo di sleep del thread dev'essere minore del tempo di refresh dei file"); 298 | } 299 | } 300 | } 301 | catch (InvalidSettingsException ise) 302 | { 303 | System.err.println("\u001B[31mErrore nelle impostazioni" + ise.getMessage() + "\u001B[0m"); 304 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m"); 305 | System.exit(1); 306 | } 307 | catch (IOException ioe) 308 | { 309 | System.err.println("\u001B[31mErrore nelle impostazioni" + ioe.getMessage() + "\u001B[0m"); 310 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m"); 311 | ioe.printStackTrace(); 312 | System.exit(1); 313 | } 314 | } 315 | 316 | private boolean isFixedNode() 317 | { 318 | String hostname; 319 | InetAddress addr; 320 | try 321 | { 322 | addr = InetAddress.getLocalHost(); 323 | hostname = addr.getHostName(); 324 | 325 | for (FixedKadNode fkn : fixedNodesList) 326 | { 327 | if (fkn.getName().toLowerCase().equals(hostname.toLowerCase())) 328 | { 329 | nodeID = fkn.getNodeID(); 330 | port = fkn.getPort(); 331 | return true; 332 | } 333 | } 334 | } 335 | catch (UnknownHostException uhe) 336 | { 337 | System.err.println("\u001B[31mErrore isFixedNode: " + uhe.getMessage() + "\u001B[0m"); 338 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m"); 339 | System.exit(1); 340 | } 341 | 342 | return false; 343 | } 344 | 345 | private void networkJoin() 346 | { 347 | //Faccio il findNode su me stesso 348 | List nearestNodes = findNode(nodeID, true); 349 | for (KadNode kn : nearestNodes) 350 | { 351 | routingTree.add(kn); 352 | } 353 | } 354 | 355 | public void writeFixedList() //poi questo sarà da chiamare da qualche parte una sola volta e poi da commentare 356 | { 357 | ArrayList fixNodes = new ArrayList<>(); 358 | try 359 | { 360 | InetAddress inAddrPunto = InetAddress.getByName("pintini.ddns.net"); 361 | String addressPunto = inAddrPunto.getHostAddress(); 362 | 363 | InetAddress inAddrTavo = InetAddress.getByName("tavolino.ddns.net"); 364 | String addressTavo = inAddrTavo.getHostAddress(); 365 | 366 | FixedKadNode Punto = new FixedKadNode("pintini.ddns.net", (int) 1336, BigInteger.ONE, "pintini"); 367 | FixedKadNode Tavolino = new FixedKadNode("tavolino.ddns.net", (int) 1336, BigInteger.valueOf(2), "tavolino"); 368 | 369 | fixNodes.add(Punto); 370 | fixNodes.add(Tavolino); 371 | } 372 | catch (UnknownHostException uhe) 373 | { 374 | System.err.println("\u001B[31mErrore nella scrittura dei nodi fissi: " + uhe.getMessage() + "\u001B[0m"); 375 | } 376 | //Scrive file "nodes", inserendoci la lista fixNodes e serializza il file 377 | FileOutputStream fout = null; 378 | ObjectOutputStream oos = null; 379 | try 380 | { 381 | fout = new FileOutputStream("." + File.separator + "nodes"); 382 | oos = new ObjectOutputStream(fout); 383 | oos.writeObject(fixNodes); 384 | } 385 | catch (IOException ioe) 386 | { 387 | System.err.println("\u001B[31mErrore nella scrittura dei nodi fissi: " + ioe.getMessage() + "\u001B[0m"); 388 | ioe.printStackTrace(); 389 | } 390 | finally 391 | { 392 | try 393 | { 394 | if (fout != null) 395 | { 396 | fout.close(); 397 | } 398 | if (oos != null) 399 | { 400 | oos.close(); 401 | } 402 | } 403 | catch (IOException ioe) 404 | { 405 | System.err.println("\u001B[31mErrore nella scrittura dei nodi fissi: " + ioe.getMessage() + "\u001B[0m"); 406 | ioe.printStackTrace(); 407 | } 408 | } 409 | } 410 | 411 | private ArrayList loadFixedNodesFromFile() //va a leggere il file serializzato e restituisce la lista di KadNodes fissi. 412 | { 413 | ArrayList retFixNodes = new ArrayList<>(); 414 | 415 | FileInputStream fis = null; 416 | try 417 | { 418 | fis = new FileInputStream("." + File.separator + "nodes"); 419 | ObjectInputStream ois = new ObjectInputStream(fis); 420 | //retFixNodes = new ArrayList<>(); ?? 421 | while (true) 422 | { 423 | retFixNodes = ((ArrayList) ois.readObject()); 424 | } 425 | } 426 | catch (EOFException e) 427 | { 428 | 429 | } 430 | catch (IOException | ClassNotFoundException ioe) 431 | { 432 | System.err.println("\u001B[31mErrore nella lettura dei nodi fissi: " + ioe.getMessage() + "\u001B[0m"); 433 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m"); 434 | ioe.printStackTrace(); 435 | System.exit(1); 436 | } 437 | finally 438 | { 439 | try 440 | { 441 | if (fis != null) 442 | { 443 | fis.close(); 444 | } 445 | } 446 | catch (IOException ioe) 447 | { 448 | } //Ignorata 449 | finally 450 | { 451 | return retFixNodes; 452 | } 453 | } 454 | } 455 | 456 | public InetAddress getIP() 457 | { 458 | String publicIP = null; 459 | try 460 | { 461 | URL urlForIP = new URL("https://api.ipify.org/"); 462 | BufferedReader in = new BufferedReader(new InputStreamReader(urlForIP.openStream())); 463 | 464 | publicIP = in.readLine(); //IP as a String 465 | } 466 | catch (MalformedURLException mue) 467 | { 468 | System.err.println("\u001B[31mErrore nell'URL per l'IP: " + mue.getMessage() + "\u001B[31m"); 469 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m"); 470 | System.exit(1); 471 | } 472 | catch (IOException ioe) 473 | { 474 | System.err.println("\u001B[31mEccezione generale nel trovare l'IP del nodo: " + ioe.getMessage() + "\u001B[0m"); 475 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m"); 476 | ioe.printStackTrace(); 477 | System.exit(1); 478 | } 479 | try 480 | { 481 | return InetAddress.getByName(publicIP); 482 | } 483 | catch (UnknownHostException e) 484 | { 485 | System.err.println("\u001B[31mHost sconosciuto nel trovare l'IP: " + e.getMessage() + "\u001B[0m"); 486 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m"); 487 | System.exit(1); 488 | return null; 489 | } 490 | } 491 | 492 | public BigInteger getNodeID() 493 | { 494 | return nodeID; 495 | } 496 | 497 | public boolean ping(KadNode node) 498 | { 499 | PingRequest pr = new PingRequest(thisNode, node); 500 | try 501 | { 502 | Socket s = new Socket(); 503 | s.setSoTimeout(timeout); 504 | s.connect(new InetSocketAddress(node.getIp(), node.getPort()), timeout); 505 | 506 | OutputStream os = s.getOutputStream(); 507 | ObjectOutputStream outputStream = new ObjectOutputStream(os); 508 | outputStream.writeObject(pr); 509 | outputStream.flush(); 510 | 511 | InputStream is = s.getInputStream(); 512 | ObjectInputStream inputStream = new ObjectInputStream(is); 513 | 514 | long timeInit = System.currentTimeMillis(); 515 | while (true) 516 | { 517 | try 518 | { 519 | Object preply = inputStream.readObject(); 520 | if (preply instanceof PingReply) 521 | { 522 | if (((PingReply) preply).getSourceKadNode().equals(pr.getDestKadNode())) 523 | { 524 | is.close(); 525 | s.close(); 526 | return true; 527 | } 528 | } 529 | s.setSoTimeout(((int) (timeout - (System.currentTimeMillis() - timeInit)))); 530 | } 531 | catch (ClassNotFoundException e) 532 | { 533 | System.err.println("\u001B[31mIl nodo pingato mi ha risposto con qualcosa che non conosco\u001B[0m"); 534 | } 535 | } 536 | } 537 | catch (SocketTimeoutException soe) 538 | { 539 | System.err.println("\u001B[31mTimeout exception: " + soe.getMessage() + "\u001B[0m"); 540 | return false; 541 | } 542 | catch (ConnectException soe) 543 | { 544 | System.err.println("\u001B[31mConnect Exception: " + soe.getMessage() + "\u001B[0m"); 545 | return false; 546 | } 547 | catch (EOFException e) 548 | { 549 | return false; 550 | } 551 | catch (IOException ex) 552 | { 553 | ex.printStackTrace(); 554 | return false; 555 | } 556 | } 557 | 558 | private Object findValue_lookup(BigInteger fileID) //Object può essere o una List oppure di tipo KadFile 559 | { 560 | /*Verifico se il file richiesto è contenuto nel nodo 561 | Controllo solo sui file ridondanti, questo per evitare che il possessore del file lo abbia modificato 562 | rispetto alla versione presente nella rete*/ 563 | KadFile temp = fileMap.get(fileID); 564 | if (temp != null) 565 | { 566 | return temp; 567 | } 568 | else 569 | { 570 | return findNode_lookup(fileID); //se non lo è procedo come per il findnode_lookup 571 | } 572 | } 573 | 574 | public Object findValue(BigInteger fileID, boolean returnContent) //Object può essere di tipo List oppure di tipo byte[] (null se returnContent=false) 575 | { 576 | Bucket bucket = routingTree.findNodesBucket(thisNode); 577 | KadNode target = new KadNode("", (short) 0, fileID); 578 | int depth = ((Node) bucket).getDepth(); 579 | BigInteger prefix = thisNode.getNodeID().shiftRight(BITID - depth); // prendo il prefisso relativo al bucket 580 | List lkn = new ArrayList<>(); // lista di tutti i nodi conosciuti 581 | List alphaNode; 582 | AbstractQueue queriedNode = new PriorityQueue<>((o1, o2) 583 | -> distanza(o1, target).compareTo(distanza(o2, target))); // lista dei nodi interrogati 584 | Iterator it = null; 585 | //System.out.println("****Prendo il lock del bucket " + bucket.getDepth()); 586 | synchronized (bucket) 587 | { 588 | it = bucket.iterator(); 589 | while (it.hasNext()) // inserisco l'intero bucket nella lista lkn 590 | { 591 | lkn.add(it.next()); 592 | } 593 | //System.out.println("<<< 0 && lkn.size() < K) // se il bucket non contiene K nodi, mi sposto negli altri bucket vicini per prendere i loro nodi fino a raggiungere K 599 | { 600 | if (prefix.testBit(depth - count)) 601 | { 602 | bucket = (Bucket) node.getRight(); 603 | } 604 | else 605 | { 606 | bucket = (Bucket) node.getLeft(); 607 | } 608 | List list = new ArrayList<>(); 609 | //System.out.println("****Prendo il lock del bucket " + bucket.getDepth()); 610 | synchronized (bucket) 611 | { 612 | it = bucket.iterator(); 613 | 614 | while (it.hasNext()) 615 | { 616 | list.add(it.next()); 617 | } 618 | //System.out.println("<<< distanza(o1, thisNode).compareTo(distanza(o2, thisNode))); 622 | if (list.size() >= K - lkn.size()) 623 | { 624 | for (KadNode kn : list.subList(0, K - lkn.size())) 625 | { 626 | lkn.add(kn); 627 | } 628 | } 629 | else 630 | { 631 | lkn.addAll(list); 632 | } 633 | node = (TreeNode) node.getParent(); 634 | count--; 635 | } 636 | lkn.sort((o1, o2) 637 | -> distanza(o1, target).compareTo(distanza(o2, target))); 638 | if (lkn.size() >= ALPHA) 639 | { 640 | alphaNode = new ArrayList<>(); 641 | for (KadNode kn : lkn.subList(0, ALPHA)) 642 | { 643 | alphaNode.add(kn); 644 | } 645 | 646 | } 647 | else 648 | { 649 | alphaNode = lkn; 650 | } 651 | //chiedo anche a me stesso 652 | //uso una coppia boolean,byte[] perchè se returnContent è false allora anche se il file è stato trovato comunque il contentuo sarà null, 653 | // boolean mi permette di capire se qualche thread ha trovato il file o meno. 654 | AtomicReference> content = new AtomicReference<>(); 655 | content.set(new Tupla(false, null)); 656 | while (true) 657 | { 658 | int size = lkn.size(); // per capire se il round di find nodes è fallito o meno 659 | //ad ognuno degli alpha node vado a inviargli un findNode 660 | // for (int i = 0; i < alphaNode.size(); i++) 661 | Thread[] threads = new Thread[alphaNode.size()]; 662 | for (int i = 0; i < alphaNode.size(); i++) 663 | { 664 | if (content.get().getKey()) 665 | { 666 | return content.get().getValue(); 667 | } 668 | KadNode kadNode = alphaNode.get(i); 669 | FindValueRequest fvr = new FindValueRequest(fileID, thisNode, kadNode, returnContent); 670 | threads[i] = new Thread(new Runnable() { 671 | @Override 672 | public void run() 673 | { 674 | try 675 | { 676 | Socket s = new Socket(); 677 | s.setSoTimeout(timeout); 678 | s.connect(new InetSocketAddress(kadNode.getIp(), kadNode.getPort()), timeout); 679 | 680 | OutputStream os = s.getOutputStream(); 681 | ObjectOutputStream outputStream = new ObjectOutputStream(os); 682 | outputStream.writeObject(fvr); 683 | outputStream.flush(); 684 | 685 | InputStream is = s.getInputStream(); 686 | ObjectInputStream inputStream = new ObjectInputStream(is); 687 | 688 | long timeInit = System.currentTimeMillis(); 689 | boolean state = true; 690 | while (state) 691 | { 692 | if (content.get().getKey()) 693 | { 694 | is.close(); 695 | s.close(); 696 | return; 697 | } 698 | try 699 | { 700 | Object fvreply = inputStream.readObject(); 701 | if (fvreply instanceof FindValueReply) 702 | { 703 | if (((FindValueReply) fvreply).getSourceKadNode().equals(fvr.getDestKadNode())) 704 | { 705 | //termino 706 | is.close(); 707 | s.close(); 708 | content.set(new Tupla(true, ((FindValueReply) fvreply).getContent())); 709 | return; 710 | } 711 | } 712 | else 713 | { 714 | if ((fvreply instanceof FindNodeReply) && ((FindNodeReply) fvreply).getSourceKadNode().equals(fvr.getDestKadNode())) 715 | { 716 | routingTree.add(((FindNodeReply) fvreply).getSourceKadNode()); 717 | Iterator it1 = ((FindNodeReply) fvreply).getList().iterator(); 718 | while (it1.hasNext()) 719 | { 720 | KadNode k = it1.next(); 721 | //System.out.println("****Prendo il lock nella lista del findNode "); 722 | synchronized ((lkn)) 723 | { 724 | if (!(lkn.contains(k))) // se mi da un nodo che conosco gia, non lo inserisco 725 | { 726 | lkn.add(k); 727 | } 728 | //System.out.println("<<< distanza(o1, target).compareTo(distanza(o2, target))); 796 | if (lkn.size() < K) 797 | { 798 | if (queriedNode.containsAll(lkn)) 799 | { 800 | return lkn; 801 | } 802 | } 803 | else 804 | { 805 | if (queriedNode.containsAll(lkn.subList(0, K))) 806 | { 807 | List toRet = new ArrayList<>(); 808 | for (KadNode kn : lkn.subList(0, K)) 809 | { 810 | toRet.add(kn); 811 | } 812 | return toRet; 813 | } 814 | } 815 | alphaNode.clear(); 816 | int alphaSize; 817 | if (size == lkn.size()) //caso in cui il round di find nodes fallisce, cioè nessuno degli alpha node mi da nuovi nodi 818 | { 819 | alphaSize = K; 820 | } 821 | else 822 | { 823 | alphaSize = ALPHA; 824 | } 825 | int i = 0; 826 | while (i < lkn.size() && alphaNode.size() < alphaSize) 827 | { 828 | if (!(queriedNode.contains(lkn.get(i)))) 829 | { 830 | alphaNode.add(lkn.get(i)); 831 | } 832 | i++; 833 | } 834 | } 835 | } 836 | 837 | private List findNode_lookup(BigInteger targetID) 838 | { 839 | Bucket bucket = routingTree.findNodesBucket(new KadNode("", (short) 0, targetID)); 840 | KadNode targetKN = new KadNode("", (short) 0, targetID); 841 | BigInteger currentID = targetID; //mi serve per tenere traccia del percorso che ho fatto nell'albero 842 | int depth = ((Node) bucket).getDepth(); 843 | List lkn = new ArrayList<>(); 844 | Iterator it = null; 845 | //System.out.println("****Prendo il lock del bucket " + bucket.getDepth()); 846 | synchronized (bucket)//lista dei K nodi conosciuti più vicini al target 847 | { 848 | it = bucket.iterator(); 849 | while (it.hasNext()) //inserisco l'intero bucket nella lista lkn (lkn conterrà i nodi (<=K) più vicini a targetID che conosco) 850 | { 851 | lkn.add(it.next()); 852 | } 853 | //System.out.println("<<<= 0 && lkn.size() < K) //ricerco altri nodi vicini al targetID finche non arrivo a K o non ho guardato tutti i nodi nell'albero 858 | { 859 | //sono ad un certo TreeNode node dell'albero, se seguo il targetID, partendo da node, e vado nello stesso ramo che raggiungo seguendo il currentID (partendo da node), 860 | // allora vuol dire che il sottoalbero relativo a quel ramo l'ho già visitato e passo a visitare il sottoalbero fratello. Altrimenti vuol dire che ho già 861 | // visitato entrambi i sottoalberi (sinistro e destro) di node e mi sposto più in su al nodo padre di node. 862 | //Sfrutto il fatto che due ID sono tanto più distanti quanto più verso sinistra è il primo bit diverso. 10111 è più distante da 11111 piuttosto che da 10000 863 | if (!(targetID.testBit((BITID - count) - 1) && currentID.testBit((BITID - count) - 1) 864 | || (!(targetID.testBit((BITID - count) - 1)) && !(currentID.testBit((BITID - count) - 1))))) 865 | { 866 | //qui ho già visitato entrambi i sottoalberi 867 | node = (TreeNode) node.getParent(); 868 | count--; 869 | } 870 | else 871 | { 872 | //qui il sottoalbero fratello non l'ho ancora visitato 873 | Node n; 874 | if (targetID.testBit((BITID - count) - 1)) //individuo se sono figlio destro o sinistro di node, poi mi sposto nel fratello per visitarlo 875 | { 876 | n = node.getRight(); 877 | } 878 | else 879 | { 880 | n = node.getLeft(); 881 | } 882 | //aggiorno currentID perchè il bit alla profondità del nodo fratello è diverso da quello del targetID, questo mi permette, quando risalgo, 883 | //di ricordarmi se ho già visitato o meno quel sottoalbero 884 | currentID = currentID.flipBit((BITID - count) - 1); 885 | if (n instanceof Bucket) 886 | { 887 | //System.out.println("****Prendo il lock del bucket " + n.getDepth()); 888 | synchronized (n) 889 | { 890 | it = ((Bucket) n).iterator(); 891 | while (it.hasNext()) 892 | { 893 | lkn.add(it.next()); 894 | } 895 | //System.out.println("<<< K) 931 | { 932 | lkn.sort((o1, o2) 933 | -> distanza(o1, targetKN).compareTo(distanza(o2, targetKN))); 934 | lkn.removeAll(lkn.subList(K, lkn.size())); 935 | } 936 | return lkn; 937 | } 938 | 939 | public List findNode(BigInteger targetID) 940 | { 941 | return findNodeMethod(targetID, true); 942 | } 943 | 944 | private List findNode(BigInteger targetID, boolean track) 945 | { 946 | return findNodeMethod(targetID, track); 947 | } 948 | 949 | private List findNodeMethod(BigInteger targetID, boolean track) 950 | { 951 | Bucket bucket = routingTree.findNodesBucket(thisNode); 952 | KadNode targetKN = new KadNode("", (short) 0, targetID); 953 | int depth = ((Node) bucket).getDepth(); 954 | BigInteger prefix = getNodeID().shiftRight(BITID - depth); // prendo il prefisso relativo al bucket 955 | List lkn = new ArrayList<>(); // lista di tutti i nodi conosciuti 956 | List alphaNode; 957 | AbstractQueue queriedNode = new PriorityQueue<>((o1, o2) 958 | -> distanza(o1, targetKN).compareTo(distanza(o2, targetKN))); // lista dei nodi interrogati 959 | Iterator it = null; 960 | //System.out.println("****Prendo il lock del bucket " + bucket.getDepth()); 961 | synchronized (bucket) 962 | { 963 | it = bucket.iterator(); 964 | while (it.hasNext()) // inserisco l'intero bucket nella lista lkn 965 | { 966 | lkn.add(it.next()); 967 | } 968 | //System.out.println("<<< 0 && lkn.size() < K) // se il bucket non contiene K nodi, mi sposto negli altri bucket vicini per prendere i loro nodi fino a raggiungere K 974 | { 975 | if (prefix.testBit(depth - count)) 976 | { 977 | bucket = (Bucket) node.getRight(); 978 | } 979 | else 980 | { 981 | bucket = (Bucket) node.getLeft(); 982 | } 983 | List list = new ArrayList<>(); 984 | //System.out.println("****Prendo il lock del bucket " + bucket.getDepth()); 985 | synchronized (bucket) 986 | { 987 | it = bucket.iterator(); 988 | 989 | while (it.hasNext()) 990 | { 991 | list.add(it.next()); 992 | } 993 | //System.out.println("<<< distanza(o1, thisNode).compareTo(distanza(o2, thisNode))); 997 | if (list.size() >= K - lkn.size()) 998 | { 999 | for (KadNode kn : list.subList(0, K - lkn.size())) 1000 | { 1001 | lkn.add(kn); 1002 | } 1003 | } 1004 | else 1005 | { 1006 | lkn.addAll(list); 1007 | } 1008 | node = (TreeNode) node.getParent(); 1009 | count--; 1010 | } 1011 | lkn.sort((o1, o2) 1012 | -> distanza(o1, targetKN).compareTo(distanza(o2, targetKN))); 1013 | if (lkn.size() >= ALPHA) 1014 | { 1015 | alphaNode = new ArrayList<>(); 1016 | for (KadNode kn : lkn.subList(0, ALPHA)) 1017 | { 1018 | alphaNode.add(kn); 1019 | } 1020 | 1021 | } 1022 | else 1023 | { 1024 | alphaNode = lkn; 1025 | } 1026 | //chiedo anche a me stesso 1027 | while (true) 1028 | { 1029 | int size = lkn.size(); // per capire se il round di find nodes è fallito o meno 1030 | //ad ognuno degli alpha node vado a inviargli un findNode 1031 | // for (int i = 0; i < alphaNode.size(); i++) 1032 | Thread[] threads = new Thread[alphaNode.size()]; 1033 | for (int i = 0; i < alphaNode.size(); i++) 1034 | { 1035 | KadNode kadNode = alphaNode.get(i); 1036 | FindNodeRequest fnr = new FindNodeRequest(targetID, thisNode, kadNode, track); 1037 | threads[i] = new Thread(() -> 1038 | { 1039 | try 1040 | { 1041 | Socket s = new Socket(); 1042 | s.setSoTimeout(timeout); 1043 | s.connect(new InetSocketAddress(kadNode.getIp(), kadNode.getPort()), timeout); 1044 | 1045 | OutputStream os = s.getOutputStream(); 1046 | ObjectOutputStream outputStream = new ObjectOutputStream(os); 1047 | outputStream.writeObject(fnr); 1048 | outputStream.flush(); 1049 | 1050 | InputStream is = s.getInputStream(); 1051 | ObjectInputStream inputStream = new ObjectInputStream(is); 1052 | 1053 | long timeInit = System.currentTimeMillis(); 1054 | boolean state = true; 1055 | while (state) 1056 | { 1057 | try 1058 | { 1059 | Object fnreply = inputStream.readObject(); 1060 | if (fnreply instanceof FindNodeReply) 1061 | { 1062 | if (((FindNodeReply) fnreply).getSourceKadNode().equals(fnr.getDestKadNode())) 1063 | { 1064 | routingTree.add(((FindNodeReply) fnreply).getSourceKadNode()); 1065 | Iterator it1 = ((FindNodeReply) fnreply).getList().iterator(); 1066 | while (it1.hasNext()) 1067 | { 1068 | KadNode k = it1.next(); 1069 | //System.out.println("****Prendo il lock nella lista del findNode "); 1070 | synchronized ((lkn)) 1071 | { 1072 | if (!(lkn.contains(k))) // se mi da un nodo che conosco gia, non lo inserisco 1073 | { 1074 | lkn.add(k); 1075 | } 1076 | //System.out.println("<<< distanza(o1, targetKN).compareTo(distanza(o2, targetKN))); 1136 | if (lkn.size() < K) 1137 | { 1138 | if (queriedNode.containsAll(lkn)) 1139 | { 1140 | return lkn; 1141 | } 1142 | } 1143 | else 1144 | { 1145 | if (queriedNode.containsAll(lkn.subList(0, K))) 1146 | { 1147 | List toRet = new ArrayList<>(); 1148 | for (KadNode kn : lkn.subList(0, K)) 1149 | { 1150 | toRet.add(kn); 1151 | } 1152 | return toRet; 1153 | } 1154 | } 1155 | alphaNode.clear(); 1156 | int alphaSize; 1157 | if (size == lkn.size()) //caso in cui il round di find nodes fallisce, cioè nessuno dei alpha node mi da nuovi nodi 1158 | { 1159 | alphaSize = K; 1160 | } 1161 | else 1162 | { 1163 | alphaSize = ALPHA; 1164 | } 1165 | int i = 0; 1166 | while (i < lkn.size() && alphaNode.size() < alphaSize) 1167 | { 1168 | if (!(queriedNode.contains(lkn.get(i)))) 1169 | { 1170 | alphaNode.add(lkn.get(i)); 1171 | } 1172 | i++; 1173 | } 1174 | } 1175 | } 1176 | 1177 | public List getFileList() 1178 | { 1179 | List temp = new ArrayList<>(); 1180 | System.out.println("[" + Thread.currentThread().getName() + "] Chiedo il lock della mappa"); 1181 | globalFileReadLock.lock(); 1182 | localFileReadLock.lock(); 1183 | System.out.println("[" + Thread.currentThread().getName() + "] Prendo il lock della mappa"); 1184 | fileMap.forEach((k, v) -> temp.add(v)); 1185 | System.out.println(fileMap.size()); 1186 | localFileMap.forEach((k, v) -> temp.add(v)); 1187 | System.out.println(localFileMap.size()); 1188 | localFileReadLock.unlock(); 1189 | globalFileReadLock.unlock(); 1190 | System.out.println("[" + Thread.currentThread().getName() + "] Lascio il lock della mappa"); 1191 | return temp; 1192 | } 1193 | 1194 | public KadNode getMyNode() 1195 | { 1196 | return thisNode; 1197 | } 1198 | 1199 | public int getPort() 1200 | { 1201 | return port; 1202 | } 1203 | 1204 | public void store(String filepath) throws FileNotFoundException, InvalidParameterException 1205 | { 1206 | File temp = new File(filepath); 1207 | if (!temp.exists()) 1208 | { 1209 | throw new FileNotFoundException(); 1210 | } 1211 | if (temp.isDirectory()) 1212 | { 1213 | throw new InvalidParameterException("Non posso memorizzare una directory"); 1214 | } 1215 | 1216 | //Genero un ID per il file e controllo se esiste 1217 | BigInteger fileID = null; 1218 | do 1219 | { 1220 | fileID = new BigInteger(BITID, new Random()); 1221 | System.out.println("Cerco se esistono file con ID: " + fileID + "..."); 1222 | } 1223 | while ((findValue(fileID, false)) instanceof byte[]); 1224 | 1225 | List closestK = findNode(fileID); 1226 | 1227 | localFileWriteLock.lock(); 1228 | System.out.println("Il file avrà ID: " + fileID); 1229 | KadFile tempfile = new KadFile(fileID, false, temp.getName(), temp.getParent()); 1230 | localFileMap.add(tempfile); 1231 | System.out.println("Invio il file a: "); 1232 | for (KadNode i : closestK) 1233 | { 1234 | System.out.println(i.getNodeID() + " (Distanza: " + distanza(new KadNode("0.0.0.0", 0, fileID), i) + ")"); 1235 | } 1236 | // List closestK = findNode_lookup(fileID); togliere il commento per i test veri 1237 | for (KadNode i : closestK) 1238 | { 1239 | try 1240 | { 1241 | StoreRequest sr = new StoreRequest(new KadFile(fileID, true, temp.getName(), temp.getParent()), thisNode, i); 1242 | new Thread(() -> 1243 | { 1244 | try 1245 | { 1246 | System.out.println("Contatto " + i.getNodeID() + "(" + i.getIp() + ") per lo store"); 1247 | Socket s = new Socket(i.getIp(), i.getPort()); 1248 | OutputStream os = s.getOutputStream(); 1249 | ObjectOutputStream outputStream = new ObjectOutputStream(os); 1250 | outputStream.writeObject(sr); 1251 | outputStream.flush(); 1252 | } 1253 | catch (IOException ex) 1254 | { 1255 | System.err.println("\u001B[31mErrore nell'invio dello store: " + ex.getMessage() + "\u001B[0m"); 1256 | //ex.printStackTrace(); 1257 | } 1258 | }).start(); 1259 | 1260 | } 1261 | catch (IOException ioe) 1262 | { 1263 | System.err.println("\u001B[31mErrore nell'eseguire lo store: " + ioe.getMessage() + "\u001B[0m"); 1264 | ioe.printStackTrace(); 1265 | } 1266 | } 1267 | localFileWriteLock.unlock(); 1268 | 1269 | } 1270 | 1271 | public void delete(BigInteger id) throws FileNotKnownException 1272 | { 1273 | localFileWriteLock.lock(); 1274 | KadFile temp = localFileMap.get(id); //Non si possono eliminare file ridondanti dalla rete, solo il proprietario può 1275 | 1276 | if (temp != null) 1277 | { 1278 | List closestK = findNode(temp.getFileID()); 1279 | System.out.println("Elimino il file " + temp.getFileName() + " (" + temp.getFileID() + ") da: "); 1280 | for (KadNode i : closestK) 1281 | { 1282 | System.out.println(i.getNodeID() + " (Distanza: " + distanza(thisNode, i) + ")"); 1283 | } 1284 | for (KadNode k : closestK) 1285 | { 1286 | DeleteRequest dr = new DeleteRequest(temp, thisNode, k); 1287 | new Thread(() -> 1288 | { 1289 | try 1290 | { 1291 | System.out.println("Contatto " + k.getNodeID() + "(" + k.getIp() + ") per il delete"); 1292 | Socket s = new Socket(); 1293 | s.setSoTimeout(timeout); 1294 | s.connect(new InetSocketAddress(k.getIp(), k.getPort()), timeout); 1295 | 1296 | OutputStream os = s.getOutputStream(); 1297 | ObjectOutputStream outputStream = new ObjectOutputStream(os); 1298 | outputStream.writeObject(dr); 1299 | outputStream.flush(); 1300 | } 1301 | catch (IOException ioe) 1302 | { 1303 | System.err.println("\u001B[31mErrore generale nell'eseguire il delete per " + k.getNodeID() + ": " + ioe.getMessage() + "\u001B[0m"); 1304 | ioe.printStackTrace(); 1305 | } 1306 | }).start(); 1307 | 1308 | } 1309 | localFileMap.remove(temp); 1310 | } 1311 | else 1312 | { 1313 | throw new FileNotKnownException(); 1314 | } 1315 | localFileWriteLock.unlock(); 1316 | } 1317 | 1318 | private class ListenerThread implements Runnable { 1319 | 1320 | private ServerSocket listener; 1321 | 1322 | @Override 1323 | public void run() 1324 | { 1325 | try 1326 | { 1327 | listener = new ServerSocket(port); 1328 | System.out.println("Thread Server avviato\n" + "IP: " + getIP() + "\nPorta: " + port); 1329 | } 1330 | catch (IOException ex) 1331 | { 1332 | System.err.println("\u001B[31mErrore nell'apertura del socket del Thread Server: " + ex.getMessage() + "\u001B[0m"); 1333 | System.err.println("\u001B[31mABORT! ABORT!\u001B[0m"); 1334 | ex.printStackTrace(); 1335 | System.exit(1); 1336 | } 1337 | 1338 | while (true) 1339 | { 1340 | Socket connection = null; 1341 | try 1342 | { 1343 | connection = listener.accept(); 1344 | 1345 | connection.setSoTimeout(timeout); 1346 | 1347 | Object responseObject = null; 1348 | 1349 | //Analizzo la richiesta ricevuta 1350 | InputStream is = connection.getInputStream(); 1351 | ObjectInputStream inStream = new ObjectInputStream(is); 1352 | 1353 | Object received = inStream.readObject(); 1354 | System.out.println(received.getClass() + " received from " + connection.getInetAddress().getHostAddress()); 1355 | 1356 | //se non ho ricevuto un pacchetto o non sono io il destinatario, chiudo la connessione 1357 | if (!(received instanceof Packet) || !((Packet) received).getDestKadNode().equals(thisNode)) 1358 | { 1359 | connection.close(); 1360 | continue; 1361 | } 1362 | 1363 | Packet p = (Packet) received; 1364 | //aggiungo il nodo sorgente all'albero di routing 1365 | if (!(received instanceof FindNodeRequest && !((FindNodeRequest) received).isTracked())) 1366 | { 1367 | new Thread(() -> 1368 | { 1369 | routingTree.add(p.getSourceKadNode()); 1370 | }).start(); 1371 | } 1372 | 1373 | //Elaboro la risposta 1374 | if (received instanceof FindNodeRequest) 1375 | { 1376 | FindNodeRequest fnr = (FindNodeRequest) received; 1377 | 1378 | System.out.println("Received FindNodeRequest from: " + fnr.getSourceKadNode()); 1379 | 1380 | List lkn = findNode_lookup(fnr.getTargetID()); 1381 | 1382 | FindNodeReply fnrep = new FindNodeReply(fnr.getTargetID(), thisNode, fnr.getSourceKadNode(), lkn); 1383 | 1384 | responseObject = fnrep; 1385 | } 1386 | else if (received instanceof FindValueRequest) 1387 | { 1388 | FindValueRequest fvr = (FindValueRequest) received; 1389 | 1390 | globalFileReadLock.lock(); 1391 | Object value = findValue_lookup(fvr.getFileID()); 1392 | globalFileReadLock.unlock(); 1393 | 1394 | FindValueReply fvrep = null; 1395 | if (value instanceof KadFile) 1396 | { 1397 | if (fvr.isContentRequested()) 1398 | { 1399 | fvrep = new FindValueReply(fvr.getFileID(), (KadFile) value, thisNode, fvr.getSourceKadNode()); 1400 | } 1401 | else 1402 | { 1403 | fvrep = new FindValueReply(fvr.getFileID(), null, thisNode, fvr.getSourceKadNode()); 1404 | } 1405 | responseObject = fvrep; 1406 | } 1407 | else //È una lista di KadNode tornata da findnode_lookup 1408 | { 1409 | responseObject = new FindNodeReply(fvr.getFileID(), thisNode, fvr.getSourceKadNode(), (List) value); 1410 | } 1411 | 1412 | } 1413 | else if (received instanceof StoreRequest) 1414 | { 1415 | System.out.println("[" + Thread.currentThread().getName() + "] Chiedo il lock della mappa"); 1416 | globalFileWriteLock.lock(); 1417 | 1418 | System.out.println("[" + Thread.currentThread().getName() + "] Prendo il lock della mappa"); 1419 | 1420 | StoreRequest rq = (StoreRequest) received; 1421 | 1422 | //i file ridondanti vengono salvati con estensione .FILEID.kad 1423 | System.out.println("Ho ricevuto uno store di " + rq.getFileName() + " da " + rq.getSourceKadNode().getIp()); 1424 | String extension = rq.getFileName().contains("." + rq.getFileID() + ".kad") ? "" : "." + rq.getFileID() + ".kad"; 1425 | File toStore = new File(FILESPATH + rq.getFileName() + extension); 1426 | toStore.delete(); 1427 | toStore.createNewFile(); 1428 | Files.write(toStore.toPath(), rq.getContent()); 1429 | fileMap.add(new KadFile(rq.getFileID(), true, rq.getFileName() + extension, FILESPATH)); 1430 | System.out.println("[" + Thread.currentThread().getName() + "] Lascio il lock della mappa"); 1431 | globalFileWriteLock.unlock(); 1432 | } 1433 | else if (received instanceof DeleteRequest) 1434 | { 1435 | System.out.println("[" + Thread.currentThread().getName() + "] Chiedo il lock della mappa"); 1436 | globalFileWriteLock.lock(); 1437 | System.out.println("[" + Thread.currentThread().getName() + "] Prendo il lock della mappa"); 1438 | 1439 | DeleteRequest dr = (DeleteRequest) received; 1440 | 1441 | System.out.println("Ho ricevuto un delete di " + dr.getFile().getFileName() + " (" + dr.getFile().getFileID() + ") da " + dr.getSourceKadNode().getIp()); 1442 | 1443 | try 1444 | { 1445 | fileMap.remove(dr.getFile().getFileID()); 1446 | } 1447 | catch (NullPointerException npe) 1448 | { 1449 | System.out.println("..Ma non ce l'ho!"); 1450 | } 1451 | //Controllo di non averlo refreshato di recente 1452 | if (recentlyRefreshed.contains(new Tupla<>(dr.getFile().getFileID(), dr.getFile().getFileName()))) 1453 | { 1454 | List temp = findNode(dr.getFile().getFileID(), true); 1455 | for (KadNode i : temp) 1456 | { 1457 | //DA PARALLELIZZARE 1458 | new Thread(() -> 1459 | { 1460 | try 1461 | { 1462 | Socket tempS = new Socket(); 1463 | tempS.setSoTimeout(timeout); 1464 | tempS.connect(new InetSocketAddress(i.getIp(), i.getPort()), timeout); 1465 | OutputStream os = tempS.getOutputStream(); 1466 | ObjectOutputStream outputStream = new ObjectOutputStream(os); 1467 | outputStream.writeObject(new DeleteRequest(dr.getFile(), thisNode, i)); 1468 | outputStream.flush(); 1469 | } 1470 | catch (IOException ex) 1471 | { 1472 | Logger.getLogger(Kademlia.class.getName()).log(Level.SEVERE, null, ex); 1473 | } 1474 | }).start(); 1475 | recentlyRefreshed.remove(new Tupla<>(dr.getFile().getFileID(), dr.getFile().getFileName())); 1476 | } 1477 | } 1478 | System.out.println("[" + Thread.currentThread().getName() + "] Lascio il lock della mappa"); 1479 | globalFileWriteLock.unlock(); 1480 | } 1481 | else if (received instanceof PingRequest) 1482 | { 1483 | PingRequest pr = (PingRequest) received; 1484 | 1485 | KadNode sourceKadNode = pr.getSourceKadNode(); 1486 | 1487 | System.out.println("Received PingRequest from: " + pr.getSourceKadNode().toString()); 1488 | 1489 | PingReply reply = new PingReply(thisNode, sourceKadNode); 1490 | 1491 | responseObject = reply; 1492 | } 1493 | 1494 | if (responseObject != null) 1495 | { 1496 | OutputStream os = connection.getOutputStream(); 1497 | ObjectOutputStream outputStream = new ObjectOutputStream(os); 1498 | try 1499 | { 1500 | System.out.println("Rispondo a " + connection.getInetAddress().getHostAddress() + " (" + ((Packet) responseObject).getDestKadNode().getNodeID() + ") con una " + responseObject.getClass()); 1501 | } 1502 | catch (NullPointerException npe) 1503 | { 1504 | System.out.println("Rispondo a " + connection.getInetAddress().getHostAddress() + " con una " + responseObject.getClass()); 1505 | } 1506 | outputStream.writeObject(responseObject); 1507 | outputStream.flush(); 1508 | os.close(); 1509 | } 1510 | } 1511 | catch (ClassNotFoundException ex) 1512 | { 1513 | System.err.println("\u001B[31mClassNotFound nel thread server: " + ex.getMessage() + "\u001B[0m"); 1514 | ex.printStackTrace(); 1515 | } 1516 | catch (IOException ex) 1517 | { 1518 | System.err.println("\u001B[31mIOException nel thread server: " + ex.getMessage() + "\u001B[0m"); 1519 | ex.printStackTrace(); 1520 | } 1521 | finally 1522 | { 1523 | try 1524 | { 1525 | if (connection != null) 1526 | { 1527 | connection.close(); 1528 | } 1529 | } 1530 | catch (IOException ex) 1531 | { 1532 | System.err.println("\u001B[31mIOException nel thread server: " + ex.getMessage() + "\u001B[0m"); 1533 | ex.printStackTrace(); 1534 | } 1535 | } 1536 | System.out.println("Connessione conclusa"); 1537 | } 1538 | } 1539 | } 1540 | 1541 | private class FileRefresh implements Runnable { 1542 | 1543 | private int refreshWait; 1544 | private int refreshThreadSleep; 1545 | 1546 | public FileRefresh(int refreshWait, int refreshThreadSleep) 1547 | { 1548 | this.refreshWait = refreshWait; 1549 | this.refreshThreadSleep = refreshThreadSleep; 1550 | } 1551 | 1552 | public void run() 1553 | { 1554 | while (true) 1555 | { 1556 | try 1557 | { 1558 | Thread.sleep(refreshThreadSleep); 1559 | System.out.println("Inizio il refresh dei file.."); 1560 | System.out.println("[" + Thread.currentThread().getName() + "] Chiedo il lock della mappa"); 1561 | List toBeDeleted = new ArrayList<>(); 1562 | 1563 | System.out.println("[" + Thread.currentThread().getName() + "] Prendo il lock della mappa"); 1564 | //si refreshano solo i file ridondanti 1565 | globalFileReadLock.lock(); 1566 | recentlyRefreshed.clear(); 1567 | List copyList = new ArrayList<>(); 1568 | fileMap.forEach((k, v) -> 1569 | { 1570 | copyList.add(v); 1571 | }); 1572 | globalFileReadLock.unlock(); 1573 | copyList.forEach((v) -> 1574 | { 1575 | try 1576 | { 1577 | sleep(5000); //aspetto 5 secondi tra un file e il successivo per lasciare libero il lock per la write 1578 | } 1579 | catch (InterruptedException ex) 1580 | { 1581 | } 1582 | globalFileReadLock.lock(); 1583 | if (fileMap.get(v.getFileID()) == null) 1584 | { 1585 | globalFileReadLock.unlock(); 1586 | return; 1587 | } 1588 | globalFileReadLock.unlock(); 1589 | if ((System.currentTimeMillis() - v.getLastRefresh()) >= refreshWait) 1590 | { 1591 | System.out.println("++++Refresho " + v.getFileName()); 1592 | List temp = findNode(v.getFileID(), true); 1593 | //Se non sono tra i K nodi più vicini a quell'ID, elimino il file da me e passo al prossimo file 1594 | boolean state = true; 1595 | for (KadNode n : temp) 1596 | { 1597 | if (thisNode.getNodeID().equals(n.getNodeID())) 1598 | { 1599 | state = false; 1600 | break; 1601 | } 1602 | } 1603 | if (state) 1604 | { 1605 | System.out.println("+++Non sono tra i nodi più vicini! Elimino il file"); 1606 | toBeDeleted.add(v); 1607 | return; 1608 | } 1609 | 1610 | recentlyRefreshed.add(new Tupla<>(v.getFileID(), v.getFileName())); 1611 | //Ora passo al refresh vero e proprio 1612 | for (KadNode n : temp) 1613 | { 1614 | System.out.println("++++Tento di contattare " + n.getNodeID() + "(" + n.getIp() + ":" + n.getPort() + ")"); 1615 | if (n.getNodeID().equals(thisNode.getNodeID())) 1616 | { 1617 | System.out.println("Ma sono io, dunque proseguo"); 1618 | continue; 1619 | } 1620 | Socket tempS = null; 1621 | try 1622 | { 1623 | tempS = new Socket(); 1624 | tempS.setSoTimeout(timeout); 1625 | tempS.connect(new InetSocketAddress(n.getIp(), n.getPort()), timeout); 1626 | OutputStream os = tempS.getOutputStream(); 1627 | ObjectOutputStream outputStream = new ObjectOutputStream(os); 1628 | outputStream.writeObject(new FindValueRequest(v.getFileID(), thisNode, n, false)); 1629 | outputStream.flush(); 1630 | 1631 | InputStream is = tempS.getInputStream(); 1632 | ObjectInputStream ois = new ObjectInputStream(is); 1633 | try 1634 | { 1635 | Object resp = ois.readObject(); 1636 | if (resp instanceof FindNodeReply) 1637 | { 1638 | KadFile toSend = new KadFile(v.getFileID(), true, v.getFileName(), v.getPath()); 1639 | System.out.println("\u001B[32m ++++Invio a " + n.getNodeID() + "(" + n.getIp() + ":" + n.getPort() + ") \u001B[0m"); 1640 | 1641 | try 1642 | { 1643 | tempS.close(); 1644 | } 1645 | catch (IOException e) 1646 | { 1647 | e.printStackTrace(); 1648 | } 1649 | tempS = new Socket(); 1650 | tempS.setSoTimeout(timeout); 1651 | tempS.connect(new InetSocketAddress(n.getIp(), n.getPort()), timeout); 1652 | os = tempS.getOutputStream(); 1653 | outputStream = new ObjectOutputStream(os); 1654 | outputStream.writeObject(new StoreRequest(toSend, thisNode, n)); 1655 | outputStream.flush(); 1656 | 1657 | System.out.println("\u001B[32m ++++Invio a " + n.getNodeID() + "(" + n.getIp() + ":" + n.getPort() + ") completato \u001B[0m"); 1658 | } 1659 | } 1660 | catch (EOFException eofe) 1661 | { 1662 | //Aspettata, ignoro 1663 | } 1664 | catch (ClassNotFoundException cnfe) 1665 | { 1666 | System.err.println("\u001B[31mErrore nella risposta durante il refresh: " + cnfe.getMessage() + "\u001B[0m"); 1667 | //Gli invio comunque il file 1668 | KadFile toSend = new KadFile(v.getFileID(), true, v.getFileName(), v.getPath()); 1669 | outputStream.writeObject(new StoreRequest(toSend, thisNode, n)); 1670 | } 1671 | } 1672 | catch (SocketException se) 1673 | { 1674 | System.err.println("\u001B[31mImpossibile aprire il socket verso " + n.getIp().toString() + ": " + se.getMessage() + "\u001B[0m"); 1675 | } 1676 | catch (EOFException eofe) 1677 | { 1678 | System.err.println("\u001B[31mEOF di: " + eofe.getCause() + "\u001B[0m"); 1679 | } 1680 | catch (IOException ioe) 1681 | { 1682 | System.err.println("\u001B[31mErrore nel refresh: " + ioe.getMessage() + "\u001B[0m"); 1683 | ioe.printStackTrace(); 1684 | } 1685 | finally 1686 | { 1687 | try 1688 | { 1689 | if (tempS != null) 1690 | { 1691 | tempS.close(); 1692 | } 1693 | } 1694 | catch (IOException ioe) 1695 | { 1696 | System.err.println("\u001B[31mErrore generale nel chiudere il socket del refresh: " + ioe.getMessage() + "\u001B[0m"); 1697 | ioe.printStackTrace(); 1698 | } 1699 | } 1700 | } 1701 | v.setLastRefresh(System.currentTimeMillis()); 1702 | } 1703 | }); 1704 | 1705 | System.out.println("[" + Thread.currentThread().getName() + "] Lascio il lock della mappa"); 1706 | System.out.println("[" + Thread.currentThread().getName() + "] Chiedo il lock della mappa"); 1707 | globalFileWriteLock.lock(); 1708 | System.out.println("[" + Thread.currentThread().getName() + "] Prendo il lock della mappa"); 1709 | //Elimino qua i file per evitare ConcurrentModificationExceptions 1710 | for (KadFile i : toBeDeleted) 1711 | { 1712 | fileMap.remove(i.getFileID()); 1713 | } 1714 | System.out.println("[" + Thread.currentThread().getName() + "] Lascio il lock della mappa"); 1715 | globalFileWriteLock.unlock(); 1716 | System.out.println("Refresh dei file finito"); 1717 | } 1718 | catch (InterruptedException ie) 1719 | { 1720 | System.err.println("\u001B[31mThread di refresh dei file interrotto\u001B[0m"); 1721 | } 1722 | } 1723 | } 1724 | } 1725 | 1726 | public static BigInteger distanza(KadNode o1, KadNode o2) 1727 | { 1728 | return o1.getNodeID().xor(o2.getNodeID()); 1729 | } 1730 | 1731 | public static String intToBinary(BigInteger n) 1732 | { 1733 | String num = ""; 1734 | for (int i = 0; i < Kademlia.BITID; i++) 1735 | { 1736 | num = (n.testBit(i) ? 1 : 0) + num; 1737 | } 1738 | return num; 1739 | } 1740 | 1741 | public String printTree() 1742 | { 1743 | StringBuilder sb = new StringBuilder(); 1744 | printTree(routingTree.getRoot(), 0, sb); 1745 | return sb.toString(); 1746 | } 1747 | 1748 | private void printTree(Node n, int indent, StringBuilder sb) 1749 | { 1750 | if (n instanceof Bucket) 1751 | { 1752 | Bucket b = (Bucket) n; 1753 | Iterator ikn = b.iterator(); 1754 | while (ikn.hasNext()) 1755 | { 1756 | StringBuilder s1 = new StringBuilder(); 1757 | for (int i = 0; i < indent; i++) 1758 | { 1759 | s1.append("| "); 1760 | } 1761 | sb.append(s1); 1762 | 1763 | sb.append("+ "); 1764 | KadNode kn = ikn.next(); 1765 | sb.append(Kademlia.intToBinary(kn.getNodeID()) + " (" + kn.getNodeID() + ") from: " + kn.getIp().toString() + ":" + kn.getPort() + "(Dist: " + distanza(thisNode, kn) + ")"); 1766 | sb.append("\n"); 1767 | } 1768 | return; 1769 | } 1770 | else 1771 | { 1772 | TreeNode tn = (TreeNode) n; 1773 | 1774 | StringBuilder s1 = new StringBuilder(); 1775 | for (int i = 0; i < indent; i++) 1776 | { 1777 | s1.append("| "); 1778 | } 1779 | sb.append(s1); 1780 | sb.append("+--"); 1781 | sb.append("1"); 1782 | sb.append("\n"); 1783 | 1784 | printTree(tn.getLeft(), indent + 1, sb); 1785 | s1 = new StringBuilder(); 1786 | for (int i = 0; i < indent; i++) 1787 | { 1788 | s1.append("| "); 1789 | } 1790 | sb.append(s1); 1791 | sb.append("+--"); 1792 | sb.append("0"); 1793 | sb.append("\n"); 1794 | printTree(tn.getRight(), indent + 1, sb); 1795 | } 1796 | } 1797 | } 1798 | --------------------------------------------------------------------------------