Merge pull request 'Étape 4' (#46) from etape4 into master
flavien's git/Projet_JAVA_P2P_STRI2A/pipeline/head This commit looks good Details

pull/83/head 1.4
Louis Royer 4 years ago
commit 07cecf2fab

5
Jenkinsfile vendored

@ -11,6 +11,10 @@
sh 'echo "BUILDING SERVER"' sh 'echo "BUILDING SERVER"'
sh 'echo Main-Class: serverP2P/ServerP2P > MANIFEST.MF' sh 'echo Main-Class: serverP2P/ServerP2P > MANIFEST.MF'
sh 'jar -cvmf MANIFEST.MF server.jar $(find bin/ -maxdepth 1 -mindepth 1 -printf "-C bin %f\n")' sh 'jar -cvmf MANIFEST.MF server.jar $(find bin/ -maxdepth 1 -mindepth 1 -printf "-C bin %f\n")'
sh 'echo "BUILDING TRACKER"'
sh 'echo Main-Class: tracker/Tracker > MANIFEST.MF'
sh 'jar -cvmf MANIFEST.MF tracker.jar $(find bin/ -maxdepth 1 -mindepth 1 -printf "-C bin %f\n")'
sh 'echo "CREATING sources.tar.gz"'
sh 'tar -zcvf sources.tar.gz src/' sh 'tar -zcvf sources.tar.gz src/'
} }
} }
@ -24,6 +28,7 @@
success { success {
archiveArtifacts artifacts: 'client.jar', fingerprint: true archiveArtifacts artifacts: 'client.jar', fingerprint: true
archiveArtifacts artifacts: 'server.jar', fingerprint: true archiveArtifacts artifacts: 'server.jar', fingerprint: true
archiveArtifacts artifacts: 'tracker.jar', fingerprint: true
archiveArtifacts artifacts: 'sources.tar.gz', fingerprint: true archiveArtifacts artifacts: 'sources.tar.gz', fingerprint: true
} }
} }

@ -6,6 +6,12 @@ Lien vers le [document original](https://stri-online.net/FTLV/mod/resource/view.
**But** : le but de ce projet est de créer une application répartie en Java de téléchargement de fichier en mode P2P (peer to peer ou poste à poste). **But** : le but de ce projet est de créer une application répartie en Java de téléchargement de fichier en mode P2P (peer to peer ou poste à poste).
Les étapes suivantes sont conseillées. Les étapes suivantes sont conseillées.
# Usage
tracker : java tracker.Tracker (interactive) or java trackerP2P.trackerP2P -- <hostname> <PORT> (default port 6969 (range 6000 -> 6999))
server : java serveurP2P.ServeurP2P (interactive) or java serveurP2P.ServeurP2P -- <serveurHOSTNAME> <serveurPORT> <trackerHOSTNAME> <trackerPORT> (default server port: server 7070 (range 7000->7070) and tracker port 6969 (range 7000 -> 7999))
client/serveur : java clientP2P.ClientP2P or java clientP2P.ClientP2P -- <clientTransportProtocol> <integratedServerHOSTNAME> <integratedServerPORT> <trackerHOSTNAME> <trackerPORT> (default tracker port 6969 (range 7000 -> 7999) and server port: server 7070 (range 7000->7070))
## Étape 1 : Téléchargement à la FTP ## Étape 1 : Téléchargement à la FTP
La première étape doit permettre de télécharger un fichier en intégralité d'une machine vers une autre machine de façon similaire aux applications suivant le protocole FTP. La première étape doit permettre de télécharger un fichier en intégralité d'une machine vers une autre machine de façon similaire aux applications suivant le protocole FTP.
@ -47,4 +53,4 @@ Options :
- Permettre la recherche de fichiers à partir de leur nom ou de toute autre caractéristique. À l'issu de la recherche on devra pouvoir connaître un ensemble d'application possédant le fichier et commencer le téléchargement. - Permettre la recherche de fichiers à partir de leur nom ou de toute autre caractéristique. À l'issu de la recherche on devra pouvoir connaître un ensemble d'application possédant le fichier et commencer le téléchargement.
- Gérer le protocole d'une application de téléchargement P2P existante (bittorrent, emule ou autre). - Gérer le protocole d'une application de téléchargement P2P existante (bittorrent, emule ou autre).
Note : toute fonctionnalité supplémentaire ne sera prise en compte dans la notation que si toutes les étapes ont été correctement traitées. Note : toute fonctionnalité supplémentaire ne sera prise en compte dans la notation que si toutes les étapes ont été correctement traitées.

@ -17,17 +17,22 @@ x bytes: [(bytes 8-?): PAYLOAD]
- `LIST` (0x00) - `LIST` (0x00)
- `LOAD` (0x01) - `LOAD` (0x01)
- `HASH` (0x02) - `HASH` (0x02)
- `DISCOVER` (0x03)
- `REGISTER` (0x04)
- `UNREGISTER` (0x05)
- RESPONSES (msb is 1): - RESPONSES (msb is 1):
- `LIST` (0x80) - `LIST` (0x80)
- `LOAD` (0x81) - `LOAD` (0x81)
- `HASH` (0x82) - `HASH` (0x82)
- `DISCOVER` (0x83)
- `VERSION ERROR` (0xC0) - `VERSION ERROR` (0xC0)
- `PROTOCOL ERROR` (0xC1) - `PROTOCOL ERROR` (0xC1)
- `INTERNAL ERROR` (0xC2) - `INTERNAL ERROR` (0xC2)
- `EMPTY DIRECTORY` (0xC3) - `EMPTY DIRECTORY` (0xC3)
- `NOT FOUND` (0xC4) - `NOT FOUND` (0xC4)
- `EMPTY FILE` (0xC5) - `EMPTY FILE` (0xC5)
- `NOT A TRACKER` (0xC6)
### List ### List
Payload size for list request is always zero. Payload size for list request is always zero.
@ -97,6 +102,55 @@ A algo hash bloc contains:
``` ```
### Tracker specific messages
#### Register
Used by a server to register itself on a tracker.
Server may want to do a free `DISCOVER` to check if they have been registered.
Payload contains:
```
2 bytes: [<PORT NUMBER>]
```
#### Unregister
Used by a server to unregister itself from a tracker.
No error is raised if the server was not registered.
Server may want to do a free `DISCOVER` to check if they have been unregistered.
Payload contains:
```
2 bytes: [<PORT NUMBER>]
? bytes: [<HOSTNAME>]
```
#### Discover request
If payload size is null, lists all servers registered.
If payload contains a filename, list all servers having this file in their list.
```
? bytes: [<FILENAME>]
? bytes: [<HOSTNAME>]
```
#### Discover response
Contains:
```
4 bytes: [(bytes 8-11): FILENAME SIZE]
y bytes: [<FILENAME>]
? bytes [multiple server blocks]
```
Server block is composed with:
```
2 bytes: [port]
? bytes: hostname
\n
```
#### Not a Tracker
This error is raised when receiving a DISCOVER, a REGISTER, or an UNREGISTER request,
but this application is not a tracker.
### Other response code (errors) ### Other response code (errors)
#### Version error #### Version error

@ -0,0 +1 @@
<mxfile host="app.diagrams.net" modified="2020-03-19T14:50:01.302Z" agent="Mozilla/5.0 (X11; Linux x86_64; rv:68.0) Gecko/20100101 Firefox/68.0" etag="dOXguTcOAZ-R9XuIfENW" version="12.8.8" type="device"><diagram name="Page-1" id="13e1069c-82ec-6db2-03f1-153e76fe0fe0">7Vtfc+I2EP80zFwfkrEt28BjQkh60+tdEtJrp2/CFqBGWFSI/LlP3xWWDZbAEJ8hZpo8MNJqvZJ2f1rtSkoL9aYvNwLPJr/zmLCW58QvLXTV8rxO2IFfRXhNCX63mxLGgsYpyV0RBvQH0URHUxc0JvMCo+ScSTorEiOeJCSSBRoWgj8X2UacFXud4TGxCIMIM5v6J43lRFNdx1k1/EroeKK77gS6YYijx7Hgi0T31/LQaPmXNk9xJkvzzyc45s9rJNRvoZ7gXKal6UuPMKXaTG3pd9dbWvNxC5LIfT5oD/EQu5E/jLz2yB86Z14q4QmzhdbFg4A5EaHHK18zHcHQZ6q4mLIvdEQYTaB2OSOCTokEfnTFNPl2Rbt8nlBJBjMcqU+fATxAm8gpg5oLRTCoxPCJyOuM4dmcDpe9OkARJFqIOX0i92Se4kZR+UKqnno5Hpasyg4k1qJyVTtLuVMa6TLDQ8Iuc8P1OOOq+4QvJzSXgj/mKFCCRjDGazylTIH7OxExTrAmayS7YMdLzOg4gUoEplhO3baNNtcTEZK8rJG0rW4IB7WJV2DRrZ6vcaPXVUdXn9dAmmF0sobPIGPEemGMc9ErcEBB42NPrCALKz1GYXLuB1YagBXfaRJW2hZWWuji2/AfZYEPtLw/Wrphk9DSsdAyIAIm8+FZmoAVt1GexQ8tsHzlkmxFSqIad0JAW9X1oaw0QyE4vNC6lHy2pllGRlJ9AKJoMn5QbVdnYcHwztENX79DQHsaGQU12Lj9RzIVf9Hf/h7Iq3+vF/0wnm6ITO/7N58HD/37T79Yxl6zpGm9IZeST6GBJPGFyhUUjfHosWxxSCzGRG5BoB4ZiQtJha3ZNdVlK0EQhiW4kUJnm1Snpd1yCiNbrcSgaCQ3NFbYnC9ERPRX63mAISg37zZBqQIsQUtL5lOsblzfMu4XMC1Q7vt3lYw7hwHLzLwc5Ga0a8qyjzcAYMmil1GnMiA6bwdEWA8gvG7Rjp5TERAo2CFoCyBAofh1jW2mGOYlA+5s7meFr1RirWgLtqNtcPtpRBkBxffUwQEUvWre5V0BuK9H0kZwzl2/bbn3miBp+qg8h32zj3J3CNoCyZWgjJGPRnNyED/WtZB19XnQ+/a9f5+ha54FsgpfacVbr6Bmwi01UjncSiGJ3s0nItMnmmHovgD0TSSbgmryiabv9dERfGK2uKycy7Pw+JFzHT/nCoL3zLk2Asa1APOTAflJbJn1+6fQqSmID8P3DeKzvPNEo/gyr9iEUL4qLKxQ/siw2HD064VMnZnE9AmKY1UshEl3yoGkLNDjGlcjQVQaGqFGux4roulWxFhgxuamoJpCI98r9oPaTum4svYt/AcKpeyLMTO/9Fb55SkE/GWusanQNnbVymmnH+4QVBO0A/PA1by6N8eFSvkPBO3/50GdVvmZc+6gUAs+AGSN232/sjd2dghqwEmJax/CpfkmsmD0kW8eP98MjY2zAfmmfc/X8Hxz9xYanNQWmu0wP72FWoJq2kJDw4G63WNsifZjhVPaEstw2YSstyrmrKx3T8zVBgv7KqDkkulkk4APD1ZvEmAG9f6O/LZTyn8Yj5fNfb/8tqH3p6WnNp09UL3vhVZ+x+o4CBW3JzumquvO1cxZg4q497rd86BcVF0PAYwTpCyQ3Dqydil/Efm2gopzOFyak0feW26E72C5RBNKxCkuk4YfbppZsFuT87cE1XXvaxxuhsc40fE8C6BfASpg1QmB32j5oh8Kj4n6r5rslYKrppXEeR2pNBA/Ee3zAXUXUIQsOGEcK7YIJ/A7JGOanFtAP/rTzK1wf8MjWMP/hBsy5MyAhQy5bYCnvgeS9rHzHo9PTiHyPK33JqbfCaq+wTNPeMMDbb3muWN2j1zR70B19Q9sKfvqnwRR/z8=</diagram></mxfile>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 29 KiB

@ -0,0 +1,409 @@
package clientP2P;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.io.IOException;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.nio.file.StandardCopyOption;
import exception.LocalException;
import exception.RemoteException;
import localException.SocketClosed;
import localException.ProtocolError;
import localException.InternalError;
import localException.TransmissionError;
import localException.SizeError;
import localException.VersionError;
import remoteException.EmptyDirectory;
import remoteException.EmptyFile;
import remoteException.VersionRemoteError;
import remoteException.ProtocolRemoteError;
import remoteException.NotFound;
import remoteException.InternalRemoteError;
import remoteException.NotATracker;
import protocolP2P.HashAlgorithm;
import protocolP2P.HashResponse;
import protocolP2P.HashRequest;
import protocolP2P.Payload;
import protocolP2P.FilePart;
import protocolP2P.LoadRequest;
import protocolP2P.ProtocolP2PPacket;
import clientP2P.ClientDownloadPart;
import tools.HostItem;
import tools.Logger;
import tools.LogLevel;
import tools.ServeErrors;
/** Class to download file
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public abstract class ClientDownload extends ServeErrors implements Runnable {
protected List<HostItem> hostList;
protected String filename;
protected byte[] hash512;
protected List<ClientDownloadPart> sockList = new ArrayList<ClientDownloadPart>();
protected List<Long> offsetsToAsk = new ArrayList<Long>();
protected List<Long> offsetsPending = new ArrayList<Long>();
protected boolean stop;
protected long size;
protected static final long MAX_PARTIAL_SIZE = 4096;
protected String partsSubdir;
protected String dirStorage;
protected boolean success = false;
protected Logger logger;
/** Constructor with parameters: filename, list of hosts, parts subdirectory and dirStorage
* @param filename name of file to download
* @param hostList list of servers
* @param partsSubdir directory to store .part files
* @param dirStorage directory to write assembled file
* @param logger Logger
*/
public ClientDownload(String filename, List<HostItem> hostList, String partsSubdir, String dirStorage, Logger logger) {
this.partsSubdir = partsSubdir;
this.dirStorage = dirStorage;
this.filename = filename;
this.hostList = hostList;
this.logger = logger;
this.stop = false;
}
/** Success getter.
* @return true when file have successfully been reassembled.
*/
public boolean getSuccess() {
return success;
}
/** Getter for hash512sum
* @return hash512sum
*/
public byte[] getHashSum512() {
return hash512;
}
/** Stop threads */
protected void stopTasks() {
for(ClientDownloadPart c : sockList) {
try {
c.setStop();
} catch (InterruptedException e) {
writeLog(e, LogLevel.Error);
}
}
}
/** Asks thread to stop
*/
public void setStop() {
stop = true;
}
/** Assign tasks randomly to threads.
* @throws InternalError
*/
protected void assignTasks() throws InternalError {
Random rand = new Random();
for(long offset : offsetsToAsk) {
try {
sockList.get(rand.nextInt(sockList.size())).assignTask(offset);
offsetsPending.add(offset);
System.err.println("Assigned task "+ offset);
writeLog("Assigned task "+ offset, LogLevel.Info);
} catch(InterruptedException e) {
writeLog(e, LogLevel.Error);
throw new InternalError();
}
}
offsetsToAsk.removeAll(offsetsPending);
}
/** Create a clientDownloadPart
* @param filename name of the file to download
* @param hostItem Hostitem of the server
*/
protected abstract ClientDownloadPart createDownloadPart(String filename, HostItem hostItem);
/** Starts threads for each server in hostList.
*/
protected void initThreads() {
for(HostItem hostItem: hostList) {
sockList.add(createDownloadPart(filename, hostItem));
}
for(ClientDownloadPart c: sockList) {
Thread t = new Thread(c);
t.start();
}
writeLog("Threads initialized", LogLevel.Info);
}
/** Remove tasks from failed threads. Update done status.
* @throws InternalError
*/
protected void checkTasksStatus() throws InternalError {
try {
synchronized(this) {
this.wait();
List<ClientDownloadPart> sockListCpy = new ArrayList<>(sockList);
for(ClientDownloadPart c: sockListCpy) {
if (c.hasFailed() == true) {
sockList.remove(c);
offsetsPending.removeAll(c.getFailed());
offsetsToAsk.addAll(c.getFailed());
}
try {
offsetsPending.removeAll(c.getDone());
} catch (InterruptedException e) {
throw new InternalError();
}
}
writeLog("Task check status: " + offsetsToAsk.size() + " to asks, " + offsetsPending.size() + " pending", LogLevel.Info);
if (offsetsToAsk.isEmpty() && offsetsPending.isEmpty()) {
stop = true;
}
if (sockList.size() == 0) {
logger.writeUDP("No thread working", LogLevel.Error);
throw new InternalError();
}
}
} catch (InterruptedException e) {
throw new InternalError();
}
}
/** Get hashsum from server.
* @param hostItem server to ask hash
* @return hash512sum
* @throws InternalError
*/
protected byte[] getHashSum512(HostItem hostItem) throws InternalError {
byte[] hash;
HashAlgorithm[] hashesAlgo = new HashAlgorithm[1];
hashesAlgo[0] = HashAlgorithm.SHA512;
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new HashRequest(filename, hashesAlgo));
try {
d.sendRequest(getHostItemSocket(hostItem));
try {
Payload pHash = d.receiveResponse().getPayload();
assert pHash instanceof HashResponse : "This payload must be instance of HashResponse";
if (!(pHash instanceof HashResponse)) {
throw new InternalError();
} else {
hash = ((HashResponse)pHash).getHash(HashAlgorithm.SHA512);
}
} catch (EmptyDirectory e) {
writeLog(e, LogLevel.Error);
hash = new byte[0];
} catch (NotFound e) {
writeLog(e, LogLevel.Error);
hash = new byte[0];
} catch (LocalException e) {
writeLog(e, LogLevel.Error);
throw new InternalError();
} catch (RemoteException e) {
writeLog(e, LogLevel.Error);
throw new InternalError();
}
return hash;
} catch (IOException e) {
writeLog(e, LogLevel.Error);
throw new InternalError();
} catch (SocketClosed e){
System.err.println("getHashSum512 : SocketClosed");
writeLog("getHashSum512 : SocketClosed", LogLevel.Error);
throw new InternalError();
}
}
/** Removes servers not owning the correct file to download from list.
* This is done by comparing hash512sum.
* @throws InternalError
*/
protected void purgeList() throws InternalError {
List<HostItem> blackList = new ArrayList<HostItem>();
boolean first = false;
byte[] hashsum;
for(HostItem host: hostList) {
// already have hashsum from 1st server
if (!first) {
first = true;
continue;
}
// ask hashsum
hashsum = getHashSum512(host);
if (!Arrays.equals(hash512, hashsum)) {
blackList.add(host);
}
}
// purge list
for(HostItem host: blackList) {
hostList.remove(host);
}
writeLog("Host list purge: done", LogLevel.Info);
}
/** Reassemble file from file parts.
* Set success to true if file is reassembled successfully.
*/
protected void reassembleFile() {
boolean firstPart = true;
boolean abort = false;
long nextOffset = 0;
do {
if (firstPart) {
writeLog("Reassembling: First part", LogLevel.Info);
try {
// create file
Files.copy(new File(partsSubdir + filename + "_" + nextOffset + ".part").toPath(), new File(dirStorage + filename).toPath(), StandardCopyOption.REPLACE_EXISTING);
nextOffset = (new File(dirStorage + filename)).length();
firstPart = false;
} catch (IOException e) {
writeLog("Reassembling: aborting on first part", LogLevel.Warning);
abort = true;
}
} else if (nextOffset >= size) {
success = true;
writeLog("Reassembling: success", LogLevel.Info);
} else {
// append to file
try {
Files.write(new File(dirStorage + filename).toPath(), Files.readAllBytes(new File(partsSubdir + filename + "_" + nextOffset + ".part").toPath()), StandardOpenOption.APPEND);
nextOffset = (new File(dirStorage + filename)).length();
} catch (IOException e) {
abort = true;
writeLog("Aborting: bad number " + nextOffset, LogLevel.Error);
}
}
} while((!success) && (!abort));
}
/** Set size of file to download. Also download first file part.
* @throws InternalError
*/
protected void setSize() throws InternalError {
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new LoadRequest(filename, 0, MAX_PARTIAL_SIZE));
try {
d.sendRequest(getHostItemSocket(hostList.get(0)));
try {
Payload p = d.receiveResponse().getPayload();
assert p instanceof FilePart : "This payload must be instance of FilePart";
if (!(p instanceof FilePart)) {
System.err.println("Error: cannot get size.");
writeLog("cannot get size.", LogLevel.Error);
throw new InternalError();
} else {
FilePart fp = (FilePart)p;
if (!fp.getFilename().equals(filename)) {
System.err.println("Error: wrong file received: `" + fp.getFilename() + "`");
writeLog("wrong file received: `" + fp.getFilename() + "`", LogLevel.Error);
throw new ProtocolError();
}
if (fp.getOffset() == 0) {
try {
Files.write(new File(partsSubdir + filename + "_0.part").toPath(), fp.getPartialContent());
} catch (IOException e) {
System.err.println("Error: cannot write file (" + partsSubdir + filename + "_0.part)");
writeLog("cannot write file (" + partsSubdir + filename + "_0.part)", LogLevel.Error);
}
size = fp.getTotalSize();
if (fp.getPartialContent().length == size) {
stop = true;
}
} else {
System.err.println("Error: wrong file part received.");
writeLog("wrong file part received.", LogLevel.Error);
throw new InternalError();
}
}
} catch (EmptyDirectory e) {
System.err.println("Error: empty directory.");
writeLog("empty directory.", LogLevel.Error);
throw new InternalError();
} catch (LocalException e) {
writeLog(e, LogLevel.Error);
throw new InternalError();
} catch (RemoteException e) {
writeLog(e, LogLevel.Error);
throw new InternalError();
}
} catch (IOException e) {
writeLog(e, LogLevel.Error);
throw new InternalError();
} catch (SocketClosed e){
System.err.println("setSize : SocketClosed");
writeLog("setSize : SocketClosed", LogLevel.Error);
}
}
/** Close HostItem socket
* @param hostItem HostItem
*/
protected abstract void closeHostItemSocket(HostItem hostItem);
/** Runnable implementation
*/
public void run() {
try {
init();
if (stop) {
writeLog("File is smaller than part max size.", LogLevel.Info);
closeHostItemSocket(hostList.get(0));
} else {
writeLog("File is bigger than part max size.", LogLevel.Info);
purgeList();
initThreads();
while(!stop) {
assignTasks();
checkTasksStatus();
}
}
writeLog("Reassembling file parts.", LogLevel.Info);
reassembleFile();
} catch(InternalError e) {
writeLog("Error while downloading file. Aborting.", LogLevel.Error);
} finally {
stopTasks();
}
}
/** Initialize infos about file to download (size, hash512sum, partslist to dl).
* Also download first partfile (to get size).
* @throws InternalError
*/
protected void init() throws InternalError {
// get size
setSize();
// get hashsum from 1st server in list
hash512 = getHashSum512(hostList.get(0));
if (hash512.length == 0) {
writeLog("no hash512sum support.", LogLevel.Error);
throw new InternalError();
}
// Add tasks
if (!stop) {
for(long i=MAX_PARTIAL_SIZE; i<size;i+=MAX_PARTIAL_SIZE) {
offsetsToAsk.add(Long.valueOf(i));
}
writeLog("Adding tasks: done", LogLevel.Info);
}
}
/** Getter for HostItem socket
* @param hostItem HostItem
*/
protected abstract Object getHostItemSocket(HostItem hostItem);
}

@ -0,0 +1,310 @@
package clientP2P;
import java.util.List;
import java.util.ArrayList;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.Payload;
import protocolP2P.LoadRequest;
import protocolP2P.FilePart;
import localException.InternalError;
import localException.ProtocolError;
import localException.TransmissionError;
import localException.VersionError;
import localException.SizeError;
import localException.SocketClosed;
import remoteException.EmptyDirectory;
import remoteException.EmptyFile;
import remoteException.InternalRemoteError;
import remoteException.VersionRemoteError;
import remoteException.ProtocolRemoteError;
import remoteException.NotFound;
import remoteException.NotATracker;
import exception.LocalException;
import exception.RemoteException;
import tools.Logger;
import tools.LogLevel;
import tools.ServeErrors;
/** Class to download file parts.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public abstract class ClientDownloadPart extends ServeErrors implements Runnable {
protected List<Long> toDoTasks;
protected List<Long> pendingTasks;
protected List<Long> tasksDone;
protected volatile boolean tasksListsLock;
protected volatile boolean stop;
protected volatile boolean failed;
protected String filename;
protected volatile boolean noTask;
protected String partsSubdir;
protected static final long MAX_PARTIAL_SIZE = 4096;
protected ClientDownload manager;
protected Logger logger;
/** Constructor with filename, socket, and part subdir
* @param filename name of file to download
* @param socket socket to use
* @param partsSubdir directory to store .part files
* @param logger Logger
*/
public ClientDownloadPart(ClientDownload manager, String filename, String partsSubdir, Logger logger) {
this.manager = manager;
this.partsSubdir = partsSubdir;
this.filename = filename;
this.logger = logger;
stop = false;
failed = false;
pendingTasks = new ArrayList<>();
toDoTasks = new ArrayList<>();
tasksDone = new ArrayList<>();
noTask = true;
tasksListsLock = false;
}
/** True if thread has failed to get a file.
* @return true if thread has failed to get a file
*/
public boolean hasFailed() {
return failed;
}
/** Asks to stop thread.
* @throws InterruptedException
*/
public synchronized void setStop() throws InterruptedException {
stop = true;
this.notifyAll();
}
/** Runnable implementation */
public void run() {
while(!stop) {
try {
doTasks();
synchronized(manager) {
manager.notify();
}
} catch(InterruptedException e) {
try {
setStop();
synchronized(manager) {
manager.notify();
}
} catch (InterruptedException e2) {
}
}
}
writeLog("Closing socket", LogLevel.Info);
try{
closeSocket();
} catch(IOException e){
writeLog("can't close socket", LogLevel.Error);
}
}
/** Close the socket
*/
protected abstract void closeSocket() throws IOException;
/** Get list of offsets that have not be downloaded if failed, else
* empty list.
* @return list of offsets
*/
public List<Long> getFailed() {
List<Long> ret = new ArrayList<>();
if (failed) {
ret.addAll(pendingTasks);
ret.addAll(toDoTasks);
}
return ret;
}
/** Get list of downloaded file parts offset, then clear this list.
* @return list of offsets
* @throws InterruptedException
*/
public List<Long> getDone() throws InterruptedException {
if (tasksDone.size() == 0) {
return new ArrayList<>();
} else {
synchronized (this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
List<Long> ret = new ArrayList<>(tasksDone);
tasksDone.clear();
tasksListsLock = false;
this.notifyAll();
return ret;
}
}
}
/** Adds offset of files parts to download.
* @param task offset to download
* @throws InterruptedException
*/
public synchronized void assignTask(Long task) throws InterruptedException {
synchronized(this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
toDoTasks.add(task);
noTask = false;
tasksListsLock = false;
this.notifyAll();
}
}
/** Send one request and wait for one response. Blocks when no task.
* @throws InterruptedException
*/
public synchronized void doTasks() throws InterruptedException {
while(noTask && !stop) {
this.wait();
}
if (!stop) {
try {
Long offset = toDoTasks.get(0);
ProtocolP2PPacket<?> p = reqPart(offset);
if (p == null) {
stop = true;
}
failed = downloadPart(p);
if (failed) {
System.err.println("Error: DownloadPart failed.");
writeLog("DownloadPart failed.", LogLevel.Error);
stop = true;
} else if (toDoTasks.isEmpty()) {
noTask = true;
}
} catch (IndexOutOfBoundsException e) {
writeLog(e, LogLevel.Error);
noTask = true;
}
}
}
/** Send a request for a specific offset.
* @param offset Offset of the file part to download
* @return ProtocolP2PPacketTCP used to send request
*/
protected ProtocolP2PPacket<?> reqPart(Long offset) {
writeLog("New request: " + offset, LogLevel.Info);
// maintain tracking of tasks
if (toDoTasks.contains(offset)) {
try {
synchronized (this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
toDoTasks.remove(offset);
pendingTasks.add(offset);
tasksListsLock = false;
this.notifyAll();
}
} catch(InterruptedException e) {
writeLog("reqPart interruptedException", LogLevel.Error);
return null;
}
} else {
writeLog("reqPart (offset " + offset + " not in toDoTasks)", LogLevel.Error);
return null;
}
// send request
try {
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new LoadRequest(filename, offset.longValue(), MAX_PARTIAL_SIZE));
d.sendRequest(getSocket());
return d;
} catch (InternalError e) {
writeLog("reqPart internalError", LogLevel.Error);
return null;
} catch (IOException e) {
writeLog("reqPart ioexception", LogLevel.Error);
writeLog(e, LogLevel.Error);
return null;
} catch (SocketClosed e){
writeLog("reqPart SocketClosed", LogLevel.Error);
return null;
}
}
/** Get the socket */
protected abstract Object getSocket();
/** Download file part associated to the request send (d).
* @param d request packet
* @return true on failure, else false
*/
public < T extends ProtocolP2PPacket<?> > boolean downloadPart(T d) {
if (d == null) {
writeLog("downloadPart -> d is null.", LogLevel.Error);
return true;
}
try {
Payload p = d.receiveResponse().getPayload();
assert p instanceof FilePart : "This payload must be instance of FilePart";
if (!(p instanceof FilePart)) {
writeLog("cannot get size.", LogLevel.Error);
return true;
} else {
FilePart fp = (FilePart)p;
if (!fp.getFilename().equals(filename)) {
writeLog("wrong file received: `" + fp.getFilename() + "`", LogLevel.Error);
return true;
}
Long offset = Long.valueOf(fp.getOffset());
if (pendingTasks.contains(offset)) {
try {
Files.write(new File(partsSubdir + filename + "_" + offset + ".part").toPath(), fp.getPartialContent());
} catch (IOException e) {
writeLog("cannot write file (" + partsSubdir + filename + "_" + offset + ".part)", LogLevel.Error);
}
} else {
writeLog("wrong file part received.", LogLevel.Error);
return true;
}
try {
synchronized(this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
pendingTasks.remove(offset);
tasksDone.add(offset);
tasksListsLock = false;
this.notifyAll();
}
} catch(InterruptedException e) {
writeLog("DownloadPart Interrupted exception", LogLevel.Error);
return true;
}
}
} catch (LocalException e) {
writeLog(e, LogLevel.Error);
return true;
} catch (RemoteException e) {
writeLog(e, LogLevel.Error);
return true;
} catch (IOException e) {
System.err.println("Error: downloadPart ioexception");
writeLog("downloadPart ioexception", LogLevel.Error);
return true;
}
return false;
}
}

@ -2,26 +2,30 @@ package clientP2P;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.net.Socket; import java.net.Socket;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import protocolP2P.ProtocolP2PPacketTCP; import protocolP2P.ProtocolP2PPacketTCP;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.Payload; import protocolP2P.Payload;
import protocolP2P.LoadRequest; import protocolP2P.LoadRequest;
import protocolP2P.FilePart; import protocolP2P.FilePart;
import exception.InternalError; import localException.InternalError;
import localException.ProtocolError;
import localException.TransmissionError;
import localException.VersionError;
import localException.SizeError;
import localException.SocketClosed;
import remoteException.EmptyDirectory; import remoteException.EmptyDirectory;
import remoteException.EmptyFile; import remoteException.EmptyFile;
import exception.ProtocolError;
import remoteException.InternalRemoteError; import remoteException.InternalRemoteError;
import remoteException.VersionRemoteError; import remoteException.VersionRemoteError;
import exception.TransmissionError;
import remoteException.ProtocolRemoteError; import remoteException.ProtocolRemoteError;
import exception.VersionError;
import exception.SizeError;
import remoteException.NotFound; import remoteException.NotFound;
import java.nio.file.Files; import remoteException.NotATracker;
import java.io.File; import tools.Logger;
import java.nio.file.Paths; import tools.LogLevel;
import java.io.IOException; import clientP2P.ClientDownloadPart;
import exception.SocketClosed;
/** Class to download file parts on tcp. /** Class to download file parts on tcp.
* @author Louis Royer * @author Louis Royer
@ -29,297 +33,55 @@ import exception.SocketClosed;
* @author JS Auge * @author JS Auge
* @version 1.0 * @version 1.0
*/ */
public class ClientDownloadPartTCP implements Runnable { public class ClientDownloadPartTCP extends ClientDownloadPart {
private List<Long> toDoTasks;
private List<Long> pendingTasks;
private List<Long> tasksDone;
private volatile boolean tasksListsLock;
private volatile boolean stop;
private volatile boolean failed;
private String filename;
private Socket socket; private Socket socket;
private volatile boolean noTask;
private String partsSubdir;
private static final long MAX_PARTIAL_SIZE = 4096;
private ClientDownloadTCP manager;
/** Constructor with filename, socket, and part subdir /** Constructor with filename, socket, and part subdir
* @param filename name of file to download * @param filename name of file to download
* @param socket socket to use * @param socket socket to use
* @param partsSubdir directory to store .part files * @param partsSubdir directory to store .part files
* @param logger Logger
*/ */
public ClientDownloadPartTCP(ClientDownloadTCP manager, String filename, Socket socket, String partsSubdir) { public ClientDownloadPartTCP(ClientDownload manager, String filename, Socket socket, String partsSubdir, Logger logger) {
this.manager = manager; super(manager, filename, partsSubdir, logger);
this.partsSubdir = partsSubdir;
this.filename = filename;
this.socket = socket; this.socket = socket;
stop = false;
failed = false;
pendingTasks = new ArrayList<>();
toDoTasks = new ArrayList<>();
tasksDone = new ArrayList<>();
noTask = true;
tasksListsLock = false;
}
/** True if thread has failed to get a file.
* @return true if thread has failed to get a file
*/
public boolean hasFailed() {
return failed;
}
/** Asks to stop thread.
* @throws InterruptedException
*/
public synchronized void setStop() throws InterruptedException {
stop = true;
this.notifyAll();
} }
/** Runnable implementation */
public void run() {
while(!stop) {
try {
doTasks();
synchronized(manager) {
manager.notify();
}
} catch(InterruptedException e) {
try {
setStop();
synchronized(manager) {
manager.notify();
}
} catch (InterruptedException e2) {
}
}
}
System.err.println("Closing socket");
try{
socket.close();
} catch(IOException e){
System.err.println("can't close socket");
}
}
/** Get list of offsets that have not be downloaded if failed, else
* empty list. /** Get the socket */
* @return list of offsets protected Object getSocket() {
*/ return (Object) socket;
public List<Long> getFailed() {
List<Long> ret = new ArrayList<>();
if (failed) {
ret.addAll(pendingTasks);
ret.addAll(toDoTasks);
}
return ret;
} }
/** Get list of downloaded file parts offset, then clear this list. /** Close the socket
* @return list of offsets
* @throws InterruptedException
*/ */
public List<Long> getDone() throws InterruptedException { protected void closeSocket() throws IOException {
if (tasksDone.size() == 0) { socket.close();
return new ArrayList<>();
} else {
synchronized (this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
List<Long> ret = new ArrayList<>(tasksDone);
tasksDone.clear();
tasksListsLock = false;
this.notifyAll();
return ret;
}
}
} }
/** Adds offset of files parts to download. /** Implementation of writeLog
* @param task offset to download * @param text Text to log
* @throws InterruptedException * @param logLevel level of logging
*/ */
public synchronized void assignTask(Long task) throws InterruptedException { protected void writeLog(String text, LogLevel logLevel) {
synchronized(this) { logger.writeTCP(text, logLevel);
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
toDoTasks.add(task);
noTask = false;
tasksListsLock = false;
this.notifyAll();
}
} }
/** Send one request and wait for one response. Blocks when no task. /** Implementation of writeLog
* @throws InterruptedException * @param e exception to log
* @param logLevel level of logging
*/ */
public synchronized void doTasks() throws InterruptedException { protected void writeLog(Exception e, LogLevel logLevel) {
while(noTask && !stop) { logger.writeTCP(e, logLevel);
this.wait();
}
if (!stop) {
try {
Long offset = toDoTasks.get(0);
ProtocolP2PPacketTCP p = reqPart(offset);
if (p == null) {
stop = true;
}
failed = downloadPart(p);
if (failed) {
System.err.println("Error: DownloadPart failed.");
stop = true;
} else if (toDoTasks.isEmpty()) {
noTask = true;
}
} catch (IndexOutOfBoundsException e) {
noTask = true;
}
}
} }
/** Send a request for a specific offset.
* @param offset Offset of the file part to download
* @return ProtocolP2PPacketTCP used to send request
*/
private ProtocolP2PPacketTCP reqPart(Long offset) {
System.err.println("New request: "+ offset);
// maintain tracking of tasks
if (toDoTasks.contains(offset)) {
try {
synchronized (this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
toDoTasks.remove(offset);
pendingTasks.add(offset);
tasksListsLock = false;
this.notifyAll();
}
} catch(InterruptedException e) {
System.err.println("Error: reqPart interruptedException");
return null;
}
} else {
System.err.println("Error: reqPart (offset " + offset + " not in toDoTasks)");
return null;
}
// send request
try {
ProtocolP2PPacketTCP d = new ProtocolP2PPacketTCP((Payload) new LoadRequest(filename, offset.longValue(), MAX_PARTIAL_SIZE));
d.sendRequest((Object)socket);
return d;
} catch (InternalError e) {
System.err.println("Error: reqPart internalError");
return null;
} catch (IOException e) {
e.printStackTrace();
System.err.println("Error: reqPart ioexception");
return null;
} catch (SocketClosed e){
System.err.println("Error: reqPart SocketClosed");
return null;
}
}
/** Download file part associated to the request send (d). /** Create packets
* @param d request packet * @param payload Payload
* @return true on failure, else false
*/ */
public boolean downloadPart(ProtocolP2PPacketTCP d) { protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
if (d == null) { return (ProtocolP2PPacket<T>)new ProtocolP2PPacketTCP<T>(payload);
System.err.println("Error: downloadPart -> d is null.");
return true;
}
try {
Payload p = d.receiveResponse().getPayload();
assert p instanceof FilePart : "This payload must be instance of FilePart";
if (!(p instanceof FilePart)) {
System.err.println("Error: cannot get size.");
return true;
} else {
FilePart fp = (FilePart)p;
if (!fp.getFilename().equals(filename)) {
System.err.println("Error: wrong file received: `" + fp.getFilename() + "`");
return true;
}
Long offset = Long.valueOf(fp.getOffset());
if (pendingTasks.contains(offset)) {
try {
Files.write(new File(partsSubdir + filename + "_" + offset + ".part").toPath(), fp.getPartialContent());
} catch (IOException e) {
System.err.println("Error: cannot write file (" + partsSubdir + filename + "_" + offset + ".part)");
}
} else {
System.err.println("Error: wrong file part received.");
return true;
}
try {
synchronized(this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
pendingTasks.remove(offset);
tasksDone.add(offset);
tasksListsLock = false;
this.notifyAll();
}
} catch(InterruptedException e) {
System.err.println("Error: DownloadPart Interrupted exception");
return true;
}
}
} catch (EmptyDirectory e) {
System.err.println("Error: empty directory.");
return true;
} catch (EmptyFile e) {
System.err.println("Error: downloadPart emptyFile");
// TODO: use more specific errors
return true;
} catch (ProtocolError e) {
System.err.println("Error: downloadPart protocolError");
return true;
} catch (InternalRemoteError e) {
System.err.println("Error: downloadPart internalRemoteError");
return true;
} catch (VersionRemoteError e) {
System.err.println("Error: downloadPart versionRemoteError");
return true;
} catch (ProtocolRemoteError e) {
System.err.println("Error: downloadPart protocolRemoteError");
return true;
} catch (TransmissionError e) {
System.err.println("Error: downloadPart transmissionError");
return true;
} catch (VersionError e) {
System.err.println("Error: downloadPart versionError");
return true;
} catch (SizeError e) {
System.err.println("Error: downloadPart sizeError");
return true;
} catch (NotFound e) {
System.err.println("Error: downloadPart notFound");
return true;
} catch (IOException e) {
System.err.println("Error: downloadPart ioexception");
return true;
} catch (InternalError e) {
System.err.println("Error: downloadPart internalError");
return true;
} catch (SocketClosed e){
System.err.println("Error: downloadPart SocketClosed");
return true;
}
return false;
} }
} }

@ -3,24 +3,28 @@ import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.net.DatagramSocket; import java.net.DatagramSocket;
import protocolP2P.ProtocolP2PPacketUDP; import protocolP2P.ProtocolP2PPacketUDP;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.Payload; import protocolP2P.Payload;
import protocolP2P.LoadRequest; import protocolP2P.LoadRequest;
import protocolP2P.FilePart; import protocolP2P.FilePart;
import exception.InternalError; import localException.InternalError;
import remoteException.EmptyDirectory; import remoteException.EmptyDirectory;
import remoteException.EmptyFile; import remoteException.EmptyFile;
import exception.ProtocolError; import localException.ProtocolError;
import remoteException.InternalRemoteError; import remoteException.InternalRemoteError;
import remoteException.VersionRemoteError; import remoteException.VersionRemoteError;
import exception.TransmissionError; import localException.TransmissionError;
import remoteException.ProtocolRemoteError; import remoteException.ProtocolRemoteError;
import exception.VersionError; import localException.VersionError;
import exception.SizeError; import localException.SizeError;
import remoteException.NotFound; import remoteException.NotFound;
import remoteException.NotATracker;
import java.nio.file.Files; import java.nio.file.Files;
import java.io.File; import java.io.File;
import java.nio.file.Paths;
import java.io.IOException; import java.io.IOException;
import tools.Logger;
import tools.LogLevel;
import clientP2P.ClientDownloadPart;
/** Class to download file parts on udp. /** Class to download file parts on udp.
* @author Louis Royer * @author Louis Royer
@ -28,287 +32,54 @@ import java.io.IOException;
* @author JS Auge * @author JS Auge
* @version 1.0 * @version 1.0
*/ */
public class ClientDownloadPartUDP implements Runnable { public class ClientDownloadPartUDP extends ClientDownloadPart {
private List<Long> toDoTasks;
private List<Long> pendingTasks;
private List<Long> tasksDone;
private volatile boolean tasksListsLock;
private volatile boolean stop;
private volatile boolean failed;
private String filename;
private DatagramSocket socket; private DatagramSocket socket;
private volatile boolean noTask;
private String partsSubdir;
private static final long MAX_PARTIAL_SIZE = 4096;
private ClientDownloadUDP manager;
/** Constructor with filename, socket, and part subdir /** Constructor with filename, socket, and part subdir
* @param filename name of file to download * @param filename name of file to download
* @param socket socket to use * @param socket socket to use
* @param partsSubdir directory to store .part files * @param partsSubdir directory to store .part files
* @param logger Logger
*/ */
public ClientDownloadPartUDP(ClientDownloadUDP manager, String filename, DatagramSocket socket, String partsSubdir) { public ClientDownloadPartUDP(ClientDownload manager, String filename, DatagramSocket socket, String partsSubdir, Logger logger) {
this.manager = manager; super(manager, filename, partsSubdir, logger);
this.partsSubdir = partsSubdir;
this.filename = filename;
this.socket = socket; this.socket = socket;
stop = false;
failed = false;
pendingTasks = new ArrayList<>();
toDoTasks = new ArrayList<>();
tasksDone = new ArrayList<>();
noTask = true;
tasksListsLock = false;
} }
/** True if thread has failed to get a file. /** Get the socket */
* @return true if thread has failed to get a file protected Object getSocket() {
*/ return (Object) socket;
public boolean hasFailed() {
return failed;
} }
/** Asks to stop thread. /** Close the socket
* @throws InterruptedException
*/ */
public synchronized void setStop() throws InterruptedException { protected void closeSocket() throws IOException {
stop = true;
this.notifyAll();
}
/** Runnable implementation */
public void run() {
while(!stop) {
try {
doTasks();
synchronized(manager) {
manager.notify();
}
} catch(InterruptedException e) {
try {
setStop();
synchronized(manager) {
manager.notify();
}
} catch (InterruptedException e2) {
}
}
}
System.err.println("Closing socket");
socket.close(); socket.close();
} }
/** Get list of offsets that have not be downloaded if failed, else
* empty list.
* @return list of offsets
*/
public List<Long> getFailed() {
List<Long> ret = new ArrayList<>();
if (failed) {
ret.addAll(pendingTasks);
ret.addAll(toDoTasks);
}
return ret;
}
/** Get list of downloaded file parts offset, then clear this list.
* @return list of offsets
* @throws InterruptedException
*/
public List<Long> getDone() throws InterruptedException {
if (tasksDone.size() == 0) {
return new ArrayList<>();
} else {
synchronized (this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
List<Long> ret = new ArrayList<>(tasksDone);
tasksDone.clear();
tasksListsLock = false;
this.notifyAll();
return ret;
}
}
}
/** Adds offset of files parts to download. /** Implementation of writeLog
* @param task offset to download * @param text Text to log
* @throws InterruptedException * @param logLevel level of logging
*/ */
public synchronized void assignTask(Long task) throws InterruptedException { protected void writeLog(String text, LogLevel logLevel) {
synchronized(this) { logger.writeUDP(text, logLevel);
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
toDoTasks.add(task);
noTask = false;
tasksListsLock = false;
this.notifyAll();
}
} }
/** Send one request and wait for one response. Blocks when no task. /** Implementation of writeLog
* @throws InterruptedException * @param e exception to log
* @param logLevel level of logging
*/ */
public synchronized void doTasks() throws InterruptedException { protected void writeLog(Exception e, LogLevel logLevel) {
while(noTask && !stop) { logger.writeUDP(e, logLevel);
this.wait();
}
if (!stop) {
try {
Long offset = toDoTasks.get(0);
ProtocolP2PPacketUDP p = reqPart(offset);
if (p == null) {
stop = true;
}
failed = downloadPart(p);
if (failed) {
System.err.println("Error: DownloadPart failed.");
stop = true;
} else if (toDoTasks.isEmpty()) {
noTask = true;
}
} catch (IndexOutOfBoundsException e) {
noTask = true;
}
}
} }
/** Send a request for a specific offset. /** Create packets
* @param offset Offset of the file part to download * @param payload Payload
* @return ProtocolP2PPacketTCP used to send request
*/ */
private ProtocolP2PPacketUDP reqPart(Long offset) { protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
System.err.println("New request: "+ offset); return (ProtocolP2PPacket<T>)new ProtocolP2PPacketUDP<T>(payload);
// maintain tracking of tasks
if (toDoTasks.contains(offset)) {
try {
synchronized (this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
toDoTasks.remove(offset);
pendingTasks.add(offset);
tasksListsLock = false;
this.notifyAll();
}
} catch(InterruptedException e) {
System.err.println("Error: reqPart interruptedException");
return null;
}
} else {
System.err.println("Error: reqPart (offset " + offset + " not in toDoTasks)");
return null;
}
// send request
try {
ProtocolP2PPacketUDP d = new ProtocolP2PPacketUDP((Payload) new LoadRequest(filename, offset.longValue(), MAX_PARTIAL_SIZE));
d.sendRequest((Object)socket);
return d;
} catch (InternalError e) {
System.err.println("Error: reqPart internalError");
return null;
} catch (IOException e) {
e.printStackTrace();
System.err.println("Error: reqPart ioexception");
return null;
}
} }
/** Download file part associated to the request send (d).
* @param d request packet
* @return true on failure, else false
*/
public boolean downloadPart(ProtocolP2PPacketUDP d) {
if (d == null) {
System.err.println("Error: downloadPart -> d is null.");
return true;
}
try {
Payload p = d.receiveResponse().getPayload();
assert p instanceof FilePart : "This payload must be instance of FilePart";
if (!(p instanceof FilePart)) {
System.err.println("Error: cannot get size.");
return true;
} else {
FilePart fp = (FilePart)p;
if (!fp.getFilename().equals(filename)) {
System.err.println("Error: wrong file received: `" + fp.getFilename() + "`");
return true;
}
Long offset = Long.valueOf(fp.getOffset());
if (pendingTasks.contains(offset)) {
try {
Files.write(new File(partsSubdir + filename + "_" + offset + ".part").toPath(), fp.getPartialContent());
} catch (IOException e) {
System.err.println("Error: cannot write file (" + partsSubdir + filename + "_" + offset + ".part)");
}
} else {
System.err.println("Error: wrong file part received.");
return true;
}
try {
synchronized(this) {
while(tasksListsLock) {
this.wait();
}
tasksListsLock = true;
pendingTasks.remove(offset);
tasksDone.add(offset);
tasksListsLock = false;
this.notifyAll();
}
} catch(InterruptedException e) {
System.err.println("Error: DownloadPart Interrupted exception");
return true;
}
}
} catch (EmptyDirectory e) {
System.err.println("Error: empty directory.");
return true;
} catch (EmptyFile e) {
System.err.println("Error: downloadPart emptyFile");
// TODO: use more specific errors
return true;
} catch (ProtocolError e) {
System.err.println("Error: downloadPart protocolError");
return true;
} catch (InternalRemoteError e) {
System.err.println("Error: downloadPart internalRemoteError");
return true;
} catch (VersionRemoteError e) {
System.err.println("Error: downloadPart versionRemoteError");
return true;
} catch (ProtocolRemoteError e) {
System.err.println("Error: downloadPart protocolRemoteError");
return true;
} catch (TransmissionError e) {
System.err.println("Error: downloadPart transmissionError");
return true;
} catch (VersionError e) {
System.err.println("Error: downloadPart versionError");
return true;
} catch (SizeError e) {
System.err.println("Error: downloadPart sizeError");
return true;
} catch (NotFound e) {
System.err.println("Error: downloadPart notFound");
return true;
} catch (IOException e) {
System.err.println("Error: downloadPart ioexception");
return true;
} catch (InternalError e) {
System.err.println("Error: downloadPart internalError");
return true;
}
return false;
}
} }

@ -1,35 +1,40 @@
package clientP2P; package clientP2P;
import clientP2P.ClientDownloadPartTCP;
import tools.HostItem;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Random; import java.util.Random;
import java.io.IOException;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.nio.file.StandardCopyOption;
import remoteException.EmptyDirectory; import remoteException.EmptyDirectory;
import remoteException.EmptyFile; import remoteException.EmptyFile;
import remoteException.VersionRemoteError; import remoteException.VersionRemoteError;
import remoteException.ProtocolRemoteError; import remoteException.ProtocolRemoteError;
import remoteException.NotFound; import remoteException.NotFound;
import remoteException.InternalRemoteError; import remoteException.InternalRemoteError;
import remoteException.NotATracker;
import localException.ProtocolError;
import localException.InternalError;
import localException.TransmissionError;
import localException.SizeError;
import localException.VersionError;
import localException.SocketClosed;
import protocolP2P.HashAlgorithm; import protocolP2P.HashAlgorithm;
import protocolP2P.HashResponse; import protocolP2P.HashResponse;
import protocolP2P.HashRequest; import protocolP2P.HashRequest;
import protocolP2P.ProtocolP2PPacketTCP; import protocolP2P.ProtocolP2PPacketTCP;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.Payload; import protocolP2P.Payload;
import exception.ProtocolError;
import exception.InternalError;
import exception.TransmissionError;
import exception.SizeError;
import exception.VersionError;
import protocolP2P.FilePart; import protocolP2P.FilePart;
import protocolP2P.LoadRequest; import protocolP2P.LoadRequest;
import java.io.IOException; import clientP2P.ClientDownloadPartTCP;
import java.nio.file.Files; import clientP2P.ClientDownload;
import java.io.File; import tools.HostItem;
import java.nio.file.Paths; import tools.Logger;
import java.nio.file.StandardOpenOption; import tools.LogLevel;
import java.nio.file.StandardCopyOption;
import exception.SocketClosed;
/** Class to download file from tcp /** Class to download file from tcp
* @author Louis Royer * @author Louis Royer
@ -37,353 +42,61 @@ import exception.SocketClosed;
* @author JS Auge * @author JS Auge
* @version 1.0 * @version 1.0
*/ */
public class ClientDownloadTCP implements Runnable { public class ClientDownloadTCP extends ClientDownload {
private List<HostItem> hostList;
private String filename;
private byte[] hash512;
private List<ClientDownloadPartTCP> sockList = new ArrayList<ClientDownloadPartTCP>();
private List<Long> offsetsToAsk = new ArrayList<Long>();
private List<Long> offsetsPending = new ArrayList<Long>();
private boolean stop;
private long size;
private static final long MAX_PARTIAL_SIZE = 4096;
private String partsSubdir;
private String dirStorage;
private boolean success = false;
/** Constructor with parameters: filename, list of hosts, parts subdirectory and dirStorage /** Constructor with parameters: filename, list of hosts, parts subdirectory and dirStorage
* @param filename name of file to download * @param filename name of file to download
* @param hostList list of servers * @param hostList list of servers
* @param partsSubdir directory to store .part files * @param partsSubdir directory to store .part files
* @param dirStorage directory to write assembled file * @param dirStorage directory to write assembled file
* @param logger Logger
*/ */
public ClientDownloadTCP(String filename, List<HostItem> hostList, String partsSubdir, String dirStorage) { public ClientDownloadTCP(String filename, List<HostItem> hostList, String partsSubdir, String dirStorage, Logger logger) {
this.partsSubdir = partsSubdir; super(filename, hostList, partsSubdir, dirStorage, logger);
this.dirStorage = dirStorage;
this.filename = filename;
this.hostList = hostList;
this.stop = false;
}
/** Asks thread to stop
*/
public void setStop() {
stop = true;
}
/** Runnable implementation
*/
public void run() {
try {
init();
if (stop) {
System.err.println("File is smaller than part max size.");
hostList.get(0).closeTCPSocket();
} else {
System.err.println("File is bigger than part max size.");
purgeList();
initThreads();
while(!stop) {
assignTasks();
checkTasksStatus();
}
}
System.err.println("Reassembling file parts.");
reassembleFile();
} catch(InternalError e) {
System.err.println("Error while downloading file. Aborting.");
} finally {
stopTasks();
}
}
/** Starts threads for each server in hostList.
*/
private void initThreads() {
for(HostItem hostItem: hostList) {
sockList.add(new ClientDownloadPartTCP(this, filename, hostItem.getTCPSocket(), partsSubdir));
}
for(ClientDownloadPartTCP c: sockList) {
Thread t = new Thread(c);
t.start();
}
System.err.println("Threads initialized");
}
/** Remove tasks from failed threads. Update done status.
* @throws InternalError
*/
private void checkTasksStatus() throws InternalError {
try {
synchronized(this) {
this.wait();
List<ClientDownloadPartTCP> sockListCpy = new ArrayList<>(sockList);
for(ClientDownloadPartTCP c: sockListCpy) {
if (c.hasFailed() == true) {
sockList.remove(c);
offsetsPending.removeAll(c.getFailed());
offsetsToAsk.addAll(c.getFailed());
}
try {
offsetsPending.removeAll(c.getDone());
} catch (InterruptedException e) {
throw new InternalError();
}
}
System.err.println("Task check status: " + offsetsToAsk.size() + " to asks, " + offsetsPending.size() + " pending");
if (offsetsToAsk.isEmpty() && offsetsPending.isEmpty()) {
stop = true;
}
if (sockList.size() == 0) {
System.err.println("No thread working");
throw new InternalError();
}
}
} catch (InterruptedException e) {
throw new InternalError();
}
}
/** Assign tasks randomly to threads.
* @throws InternalError
*/
private void assignTasks() throws InternalError {
Random rand = new Random();
for(long offset : offsetsToAsk) {
try {
sockList.get(rand.nextInt(sockList.size())).assignTask(offset);
offsetsPending.add(offset);
System.err.println("Assigned task "+ offset);
} catch(InterruptedException e) {
throw new InternalError();
}
}
offsetsToAsk.removeAll(offsetsPending);
}
/** Stop threads */
private void stopTasks() {
for(ClientDownloadPartTCP c : sockList) {
try {
c.setStop();
} catch (InterruptedException e) {}
}
}
/** Get hashsum from server.
* @param hostItem server to ask hash
* @return hash512sum
* @throws InternalError
*/
private byte[] getHashSum512(HostItem hostItem) throws InternalError {
byte[] hash;
HashAlgorithm[] hashesAlgo = new HashAlgorithm[1];
hashesAlgo[0] = HashAlgorithm.SHA512;
ProtocolP2PPacketTCP d = new ProtocolP2PPacketTCP((Payload) new HashRequest(filename, hashesAlgo));
try {
d.sendRequest((Object)hostItem.getTCPSocket());
try {
Payload pHash = d.receiveResponse().getPayload();
assert pHash instanceof HashResponse : "This payload must be instance of HashResponse";
if (!(pHash instanceof HashResponse)) {
throw new InternalError();
} else {
hash = ((HashResponse)pHash).getHash(HashAlgorithm.SHA512);
}
} catch (EmptyDirectory e) {
hash = new byte[0];
} catch (NotFound e) {
hash = new byte[0];
// TODO: use more specific errors
} catch (EmptyFile e) {
throw new InternalError();
} catch (ProtocolError e) {
throw new InternalError();
} catch (InternalRemoteError e) {
throw new InternalError();
} catch (VersionRemoteError e) {
throw new InternalError();
} catch (ProtocolRemoteError e) {
throw new InternalError();
} catch (TransmissionError e) {
throw new InternalError();
} catch (VersionError e) {
throw new InternalError();
} catch (SizeError e) {
throw new InternalError();
}
return hash;
} catch (IOException e) {
throw new InternalError();
} catch (SocketClosed e){
System.err.println("getHashSum512 : SocketClosed");
throw new InternalError();
}
} }
/** Removes servers not owning the correct file to download from list. /** Create a clientDownloadPart
* This is done by comparing hash512sum. * @param filename name of the file to download
* @throws InternalError * @param hostItem Hostitem of the server
*/ */
private void purgeList() throws InternalError { protected ClientDownloadPart createDownloadPart(String filename, HostItem hostItem) {
List<HostItem> blackList = new ArrayList<HostItem>(); return (ClientDownloadPart)new ClientDownloadPartTCP((ClientDownload)this, filename, hostItem.getTCPSocket(), partsSubdir, logger);
boolean first = false;
byte[] hashsum;
for(HostItem host: hostList) {
// already have hashsum from 1st server
if (!first) {
first = true;
continue;
}
// ask hashsum
hashsum = getHashSum512(host);
if (!Arrays.equals(hash512, hashsum)) {
blackList.add(host);
}
}
// purge list
for(HostItem host: blackList) {
hostList.remove(host);
}
System.err.println("Host list purge: done");
} }
/** Getter for hash512sum /** Close HostItem socket
* @return hash512sum * @param hostItem HostItem
*/ */
public byte[] getHashSum512() { protected void closeHostItemSocket(HostItem hostItem) {
return hash512; hostItem.closeTCPSocket();
} }
/** Initialize infos about file to download (size, hash512sum, partslist to dl). /** Implementation of writeLog
* Also download first partfile (to get size). * @param text Text to log
* @throws InternalError * @param logLevel level of logging
*/ */
private void init() throws InternalError { protected void writeLog(String text, LogLevel logLevel) {
// get size logger.writeTCP(text, logLevel);
setSize();
// get hashsum from 1st server in list
hash512 = getHashSum512(hostList.get(0));
if (hash512.length == 0) {
System.err.println("Error: no hash512sum support.");
throw new InternalError();
}
// Add tasks
if (!stop) {
for(long i=MAX_PARTIAL_SIZE; i<size;i+=MAX_PARTIAL_SIZE) {
offsetsToAsk.add(Long.valueOf(i));
}
System.err.println("Adding tasks: done");
}
} }
/** Set size of file to download. Also download first file part. /** Implementation of writeLog
* @throws InternalError * @param e exception to log
* @param logLevel level of logging
*/ */
private void setSize() throws InternalError { protected void writeLog(Exception e, LogLevel logLevel) {
ProtocolP2PPacketTCP d = new ProtocolP2PPacketTCP((Payload) new LoadRequest(filename, 0, MAX_PARTIAL_SIZE)); logger.writeTCP(e, logLevel);
try {
d.sendRequest((Object)hostList.get(0).getTCPSocket());
try {
Payload p = d.receiveResponse().getPayload();
assert p instanceof FilePart : "This payload must be instance of FilePart";
if (!(p instanceof FilePart)) {
System.err.println("Error: cannot get size.");
throw new InternalError();
} else {
FilePart fp = (FilePart)p;
if (!fp.getFilename().equals(filename)) {
System.err.println("Error: wrong file received: `" + fp.getFilename() + "`");
throw new ProtocolError();
}
if (fp.getOffset() == 0) {
try {
Files.write(new File(partsSubdir + filename + "_0.part").toPath(), fp.getPartialContent());
} catch (IOException e) {
System.err.println("Error: cannot write file (" + partsSubdir + filename + "_0.part)");
}
size = fp.getTotalSize();
if (fp.getPartialContent().length == size) {
stop = true;
}
} else {
System.err.println("Error: wrong file part received.");
throw new InternalError();
}
}
} catch (EmptyDirectory e) {
System.err.println("Error: empty directory.");
throw new InternalError();
} catch (EmptyFile e) {
// TODO: use more specific errors
throw new InternalError();
} catch (ProtocolError e) {
throw new InternalError();
} catch (InternalRemoteError e) {
throw new InternalError();
} catch (VersionRemoteError e) {
throw new InternalError();
} catch (ProtocolRemoteError e) {
throw new InternalError();
} catch (TransmissionError e) {
throw new InternalError();
} catch (VersionError e) {
throw new InternalError();
} catch (SizeError e) {
throw new InternalError();
} catch (NotFound e) {
throw new InternalError();
}
} catch (IOException e) {
throw new InternalError();
} catch (SocketClosed e){
System.err.println("setSize : SocketClosed");
}
} }
/** Success getter. /** Create packets
* @return true when file have successfully been reassembled. * @param payload Payload
*/ */
public boolean getSuccess() { protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
return success; return (ProtocolP2PPacket<T>)new ProtocolP2PPacketTCP<T>(payload);
} }
/** Reassemble file from file parts. /** Getter for HostItem socket
* Set success to true if file is reassembled successfully. * @param hostItem HostItem
*/ */
private void reassembleFile() { protected Object getHostItemSocket(HostItem hostItem) {
boolean firstPart = true; return (Object)hostItem.getTCPSocket();
boolean abort = false;
long nextOffset = 0;
do {
if (firstPart) {
System.err.println("Reassembling: First part");
try {
// create file
Files.copy(new File(partsSubdir + filename + "_" + nextOffset + ".part").toPath(), new File(dirStorage + filename).toPath(), StandardCopyOption.REPLACE_EXISTING);
nextOffset = (new File(dirStorage + filename)).length();
firstPart = false;
} catch (IOException e) {
System.err.println("Reassembling: aborting on first part");
abort = true;
}
} else if (nextOffset >= size) {
success = true;
System.err.println("Reassembling: success");
} else {
// append to file
try {
Files.write(new File(dirStorage + filename).toPath(), Files.readAllBytes(new File(partsSubdir + filename + "_" + nextOffset + ".part").toPath()), StandardOpenOption.APPEND);
nextOffset = (new File(dirStorage + filename)).length();
} catch (IOException e) {
abort = true;
System.err.println("Aborting: bad number " + nextOffset);
}
}
} while((!success) && (!abort));
} }
} }

@ -1,34 +1,40 @@
package clientP2P; package clientP2P;
import clientP2P.ClientDownloadPartUDP;
import tools.HostItem;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Random; import java.util.Random;
import java.io.IOException;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.nio.file.StandardCopyOption;
import localException.ProtocolError;
import localException.InternalError;
import localException.TransmissionError;
import localException.SizeError;
import localException.VersionError;
import remoteException.EmptyDirectory; import remoteException.EmptyDirectory;
import remoteException.EmptyFile; import remoteException.EmptyFile;
import remoteException.VersionRemoteError; import remoteException.VersionRemoteError;
import remoteException.ProtocolRemoteError; import remoteException.ProtocolRemoteError;
import remoteException.NotFound; import remoteException.NotFound;
import remoteException.InternalRemoteError; import remoteException.InternalRemoteError;
import remoteException.NotATracker;
import protocolP2P.HashAlgorithm; import protocolP2P.HashAlgorithm;
import protocolP2P.HashResponse; import protocolP2P.HashResponse;
import protocolP2P.HashRequest; import protocolP2P.HashRequest;
import protocolP2P.ProtocolP2PPacketUDP; import protocolP2P.ProtocolP2PPacketUDP;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.Payload; import protocolP2P.Payload;
import exception.ProtocolError;
import exception.InternalError;
import exception.TransmissionError;
import exception.SizeError;
import exception.VersionError;
import protocolP2P.FilePart; import protocolP2P.FilePart;
import protocolP2P.LoadRequest; import protocolP2P.LoadRequest;
import java.io.IOException; import clientP2P.ClientDownloadPartUDP;
import java.nio.file.Files; import clientP2P.ClientDownload;
import java.io.File; import tools.HostItem;
import java.nio.file.Paths; import tools.Logger;
import java.nio.file.StandardOpenOption; import tools.LogLevel;
import java.nio.file.StandardCopyOption;
/** Class to download file from udp /** Class to download file from udp
* @author Louis Royer * @author Louis Royer
@ -36,348 +42,61 @@ import java.nio.file.StandardCopyOption;
* @author JS Auge * @author JS Auge
* @version 1.0 * @version 1.0
*/ */
public class ClientDownloadUDP implements Runnable { public class ClientDownloadUDP extends ClientDownload {
private List<HostItem> hostList;
private String filename;
private byte[] hash512;
private List<ClientDownloadPartUDP> sockList = new ArrayList<ClientDownloadPartUDP>();
private List<Long> offsetsToAsk = new ArrayList<Long>();
private List<Long> offsetsPending = new ArrayList<Long>();
private boolean stop;
private long size;
private static final long MAX_PARTIAL_SIZE = 4096;
private String partsSubdir;
private String dirStorage;
private boolean success = false;
/** Constructor with parameters: filename, list of hosts, parts subdirectory and dirStorage /** Constructor with parameters: filename, list of hosts, parts subdirectory and dirStorage
* @param filename name of file to download * @param filename name of file to download
* @param hostList list of servers * @param hostList list of servers
* @param partsSubdir directory to store .part files * @param partsSubdir directory to store .part files
* @param dirStorage directory to write assembled file * @param dirStorage directory to write assembled file
* @param logger Logger
*/ */
public ClientDownloadUDP(String filename, List<HostItem> hostList, String partsSubdir, String dirStorage) { public ClientDownloadUDP(String filename, List<HostItem> hostList, String partsSubdir, String dirStorage, Logger logger) {
this.partsSubdir = partsSubdir; super(filename, hostList, partsSubdir, dirStorage, logger);
this.dirStorage = dirStorage;
this.filename = filename;
this.hostList = hostList;
this.stop = false;
}
/** Asks thread to stop
*/
public void setStop() {
stop = true;
}
/** Runnable implementation
*/
public void run() {
try {
init();
if (stop) {
System.err.println("File is smaller than part max size.");
hostList.get(0).closeUDPSocket();
} else {
System.err.println("File is bigger than part max size.");
purgeList();
initThreads();
while(!stop) {
assignTasks();
checkTasksStatus();
}
}
System.err.println("Reassembling file parts.");
reassembleFile();
} catch(InternalError e) {
System.err.println("Error while downloading file. Aborting.");
} finally {
stopTasks();
}
}
/** Starts threads for each server in hostList.
*/
private void initThreads() {
for(HostItem hostItem: hostList) {
sockList.add(new ClientDownloadPartUDP(this, filename, hostItem.getUDPSocket(), partsSubdir));
}
for(ClientDownloadPartUDP c: sockList) {
Thread t = new Thread(c);
t.start();
}
System.err.println("Threads initialized");
}
/** Remove tasks from failed threads. Update done status.
* @throws InternalError
*/
private void checkTasksStatus() throws InternalError {
try {
synchronized(this) {
this.wait();
List<ClientDownloadPartUDP> sockListCpy = new ArrayList<>(sockList);
for(ClientDownloadPartUDP c: sockListCpy) {
if (c.hasFailed() == true) {
sockList.remove(c);
offsetsPending.removeAll(c.getFailed());
offsetsToAsk.addAll(c.getFailed());
}
try {
offsetsPending.removeAll(c.getDone());
} catch (InterruptedException e) {
throw new InternalError();
}
}
System.err.println("Task check status: " + offsetsToAsk.size() + " to asks, " + offsetsPending.size() + " pending");
if (offsetsToAsk.isEmpty() && offsetsPending.isEmpty()) {
stop = true;
}
if (sockList.size() == 0) {
System.err.println("No thread working");
throw new InternalError();
}
}
} catch (InterruptedException e) {
throw new InternalError();
}
}
/** Assign tasks randomly to threads.
* @throws InternalError
*/
private void assignTasks() throws InternalError {
Random rand = new Random();
for(long offset : offsetsToAsk) {
try {
sockList.get(rand.nextInt(sockList.size())).assignTask(offset);
offsetsPending.add(offset);
System.err.println("Assigned task "+ offset);
} catch(InterruptedException e) {
throw new InternalError();
}
}
offsetsToAsk.removeAll(offsetsPending);
}
/** Stop threads */
private void stopTasks() {
for(ClientDownloadPartUDP c : sockList) {
try {
c.setStop();
} catch (InterruptedException e) {}
}
}
/** Get hashsum from server.
* @param hostItem server to ask hash
* @return hash512sum
* @throws InternalError
*/
private byte[] getHashSum512(HostItem hostItem) throws InternalError {
byte[] hash;
HashAlgorithm[] hashesAlgo = new HashAlgorithm[1];
hashesAlgo[0] = HashAlgorithm.SHA512;
ProtocolP2PPacketUDP d = new ProtocolP2PPacketUDP((Payload) new HashRequest(filename, hashesAlgo));
try {
d.sendRequest((Object)hostItem.getUDPSocket());
try {
Payload pHash = d.receiveResponse().getPayload();
assert pHash instanceof HashResponse : "This payload must be instance of HashResponse";
if (!(pHash instanceof HashResponse)) {
throw new InternalError();
} else {
hash = ((HashResponse)pHash).getHash(HashAlgorithm.SHA512);
}
} catch (EmptyDirectory e) {
hash = new byte[0];
} catch (NotFound e) {
hash = new byte[0];
// TODO: use more specific errors
} catch (EmptyFile e) {
throw new InternalError();
} catch (ProtocolError e) {
throw new InternalError();
} catch (InternalRemoteError e) {
throw new InternalError();
} catch (VersionRemoteError e) {
throw new InternalError();
} catch (ProtocolRemoteError e) {
throw new InternalError();
} catch (TransmissionError e) {
throw new InternalError();
} catch (VersionError e) {
throw new InternalError();
} catch (SizeError e) {
throw new InternalError();
}
return hash;
} catch (IOException e) {
throw new InternalError();
}
} }
/** Removes servers not owning the correct file to download from list. /** Create a clientDownloadPart
* This is done by comparing hash512sum. * @param filename name of the file to download
* @throws InternalError * @param hostItem Hostitem of the server
*/ */
private void purgeList() throws InternalError { protected ClientDownloadPart createDownloadPart(String filename, HostItem hostItem) {
List<HostItem> blackList = new ArrayList<HostItem>(); return (ClientDownloadPart)new ClientDownloadPartUDP((ClientDownload)this, filename, hostItem.getUDPSocket(), partsSubdir, logger);
boolean first = false;
byte[] hashsum;
for(HostItem host: hostList) {
// already have hashsum from 1st server
if (!first) {
first = true;
continue;
}
// ask hashsum
hashsum = getHashSum512(host);
if (!Arrays.equals(hash512, hashsum)) {
blackList.add(host);
}
}
// purge list
for(HostItem host: blackList) {
hostList.remove(host);
}
System.err.println("Host list purge: done");
} }
/** Getter for hash512sum /** Implementation of writeLog
* @return hash512sum * @param text Text to log
* @param logLevel level of logging
*/ */
public byte[] getHashSum512() { protected void writeLog(String text, LogLevel logLevel) {
return hash512; logger.writeUDP(text, logLevel);
} }
/** Initialize infos about file to download (size, hash512sum, partslist to dl). /** Implementation of writeLog
* Also download first partfile (to get size). * @param e exception to log
* @throws InternalError * @param logLevel level of logging
*/ */
private void init() throws InternalError { protected void writeLog(Exception e, LogLevel logLevel) {
// get size logger.writeUDP(e, logLevel);
setSize();
// get hashsum from 1st server in list
hash512 = getHashSum512(hostList.get(0));
if (hash512.length == 0) {
System.err.println("Error: no hash512sum support.");
throw new InternalError();
}
// Add tasks
if (!stop) {
for(long i=MAX_PARTIAL_SIZE; i<size;i+=MAX_PARTIAL_SIZE) {
offsetsToAsk.add(Long.valueOf(i));
}
System.err.println("Adding tasks: done");
}
} }
/** Set size of file to download. Also download first file part. /** Create packets
* @throws InternalError * @param payload Payload
*/ */
private void setSize() throws InternalError { protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
ProtocolP2PPacketUDP d = new ProtocolP2PPacketUDP((Payload) new LoadRequest(filename, 0, MAX_PARTIAL_SIZE)); return (ProtocolP2PPacket<T>)new ProtocolP2PPacketUDP<T>(payload);
try {
d.sendRequest((Object)hostList.get(0).getUDPSocket());
try {
Payload p = d.receiveResponse().getPayload();
assert p instanceof FilePart : "This payload must be instance of FilePart";
if (!(p instanceof FilePart)) {
System.err.println("Error: cannot get size.");
throw new InternalError();
} else {
FilePart fp = (FilePart)p;
if (!fp.getFilename().equals(filename)) {
System.err.println("Error: wrong file received: `" + fp.getFilename() + "`");
throw new ProtocolError();
}
if (fp.getOffset() == 0) {
try {
Files.write(new File(partsSubdir + filename + "_0.part").toPath(), fp.getPartialContent());
} catch (IOException e) {
System.err.println("Error: cannot write file (" + partsSubdir + filename + "_0.part)");
}
size = fp.getTotalSize();
if (fp.getPartialContent().length == size) {
stop = true;
}
} else {
System.err.println("Error: wrong file part received.");
throw new InternalError();
}
}
} catch (EmptyDirectory e) {
System.err.println("Error: empty directory.");
throw new InternalError();
} catch (EmptyFile e) {
// TODO: use more specific errors
throw new InternalError();
} catch (ProtocolError e) {
throw new InternalError();
} catch (InternalRemoteError e) {
throw new InternalError();
} catch (VersionRemoteError e) {
throw new InternalError();
} catch (ProtocolRemoteError e) {
throw new InternalError();
} catch (TransmissionError e) {
throw new InternalError();
} catch (VersionError e) {
throw new InternalError();
} catch (SizeError e) {
throw new InternalError();
} catch (NotFound e) {
throw new InternalError();
}
} catch (IOException e) {
throw new InternalError();
}
} }
/** Success getter. /** Getter for HostItem socket
* @return true when file have successfully been reassembled. * @param hostItem HostItem
*/ */
public boolean getSuccess() { protected Object getHostItemSocket(HostItem hostItem) {
return success; return (Object)hostItem.getUDPSocket();
} }
/** Reassemble file from file parts. /** Close HostItem socket
* Set success to true if file is reassembled successfully. * @param hostItem HostItem
*/ */
private void reassembleFile() { protected void closeHostItemSocket(HostItem hostItem) {
boolean firstPart = true; hostItem.closeUDPSocket();
boolean abort = false;
long nextOffset = 0;
do {
if (firstPart) {
System.err.println("Reassembling: First part");
try {
// create file
Files.copy(new File(partsSubdir + filename + "_" + nextOffset + ".part").toPath(), new File(dirStorage + filename).toPath(), StandardCopyOption.REPLACE_EXISTING);
nextOffset = (new File(dirStorage + filename)).length();
firstPart = false;
} catch (IOException e) {
System.err.println("Reassembling: aborting on first part");
abort = true;
}
} else if (nextOffset >= size) {
success = true;
System.err.println("Reassembling: success");
} else {
// append to file
try {
Files.write(new File(dirStorage + filename).toPath(), Files.readAllBytes(new File(partsSubdir + filename + "_" + nextOffset + ".part").toPath()), StandardOpenOption.APPEND);
nextOffset = (new File(dirStorage + filename)).length();
} catch (IOException e) {
abort = true;
System.err.println("Aborting: bad number " + nextOffset);
}
}
} while((!success) && (!abort));
} }
} }

@ -0,0 +1,262 @@
package clientP2P;
import tools.HostItem;
import tools.Logger;
import tools.LogLevel;
import java.util.Scanner;
import java.util.List;
import localException.ProtocolError;
import tools.ServeErrors;
import protocolP2P.RequestResponseCode;
import protocolP2P.FileList;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.DiscoverRequest;
import protocolP2P.DiscoverResponse;
import protocolP2P.Payload;
import protocolP2P.HashAlgorithm;
import localException.InternalError;
import localException.ProtocolError;
import localException.SizeError;
import localException.TransmissionError;
import localException.VersionError;
import localException.SocketClosed;
import remoteException.EmptyFile;
import remoteException.EmptyDirectory;
import remoteException.InternalRemoteError;
import remoteException.NotFound;
import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.NotATracker;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
/** Implementation of P2P-JAVA-PROJECT CLIENT
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public abstract class ClientManagement extends ServeErrors implements Runnable {
protected String baseDirectory;
protected String partsSubdir;
protected List<HostItem> hostList;
protected HostItem tracker;
protected Logger logger;
protected Scanner scanner;
protected ClientDownload downLoader;
/** Constructor with baseDirectory, tracker, partsSubdir, logger, and scanner parameters.
* @param baseDirectory the root directory where files are stored
* @param tracker Tracker hostItem
* @param partsSubdir subdirectory to store file parts
* @param logger Loggger
* @param scanner Scanner used to read input
*/
public ClientManagement(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, Scanner scanner) {
this.scanner = scanner;
this.baseDirectory = baseDirectory;
this.tracker = tracker;
this.partsSubdir = partsSubdir;
this.logger = logger;
try {
initHostList();
} catch (InternalError e) {
System.exit(-1);
} catch (ProtocolError e) {
System.exit(-2);
}
}
/** Getter for tracker socket
*/
protected abstract Object getTrackerSocket();
/** Initialize hostList from tracker
* @throws ProtocolError
* @throws InternalError
*/
private void initHostList() throws ProtocolError, InternalError {
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new DiscoverRequest(null));
try {
d.sendRequest(getTrackerSocket());
Payload p = d.receiveResponse().getPayload();
assert p instanceof DiscoverResponse : "This payload must be instance of Filelist";
if (!(p instanceof DiscoverResponse)) {
throw new InternalError();
} else {
hostList = ((DiscoverResponse)p).getHostList();
}
} catch (SocketClosed e){
writeLog("listDirectory : SocketClosed", LogLevel.Error);
throw new ProtocolError();
} catch (NotATracker e) {
writeLog(e, LogLevel.Error);
throw new ProtocolError();
} catch (Exception e) {
writeLog(e, LogLevel.Error);
throw new ProtocolError();
}
}
/** Compute Hashsum of a file.
* @param filename
* @return hashsum
*/
protected byte[] computeHashsum(String filename, HashAlgorithm h) {
try {
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
return md.digest(Files.readAllBytes(Paths.get(baseDirectory + filename)));
} catch (NoSuchAlgorithmException e) {
writeLog(h.getName() + " not supported", LogLevel.Error);
} catch (IOException e) {
writeLog("cannot read " + filename, LogLevel.Error);
}
return new byte[0];
}
/** Getter for HostItem socket
* @param hostItem HostItem
*/
protected abstract Object getHostItemSocket(HostItem hostItem);
/** list servers directory content
* @return list of files
* @throws InternalError
* @throws UnknowHostException
* @throws IOException
* @throws TransmissionError
* @throws ProtocolError
* @throws VersionError
* @throws SizeError
* @throws EmptyDirectory
* @throws InternalRemoteError
* @throws ProtocolRemoteError
* @throws VersionRemoteError
*/
protected String[] listDirectory() throws EmptyDirectory, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError {
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new Payload(RequestResponseCode.LIST_REQUEST));
try {
d.sendRequest(getHostItemSocket(hostList.get(0)));
Payload p = d.receiveResponse().getPayload();
assert p instanceof FileList : "This payload must be instance of Filelist";
if (!(p instanceof FileList)) {
throw new InternalError();
} else {
return ((FileList)p).getFileList();
}
} catch (NotFound e) {
writeLog(e, LogLevel.Error);
throw new ProtocolError();
} catch (EmptyFile e) {
writeLog(e, LogLevel.Error);
throw new ProtocolError();
} catch (SocketClosed e){
writeLog("listDirectory : SocketClosed", LogLevel.Error);
throw new ProtocolError();
} catch (NotATracker e) {
writeLog(e, LogLevel.Error);
throw new ProtocolError();
}
}
/** Initialize downloader
* @param filename Name of the file to download
*/
protected abstract void initDownloader(String filename);
/** Try to download a file
* @param filename name of the file to download
* @throws NotFound
* @throws InternalError
* @throws UnknownHostException
* @throws IOException
* @throws TransmissionError
* @throws ProtocolError
* @throws VersionError
* @throws SizeError
* @throws InternalRemoteError
* @throws ProtocolRemoteError
* @throws VersionRemoteError
* @throws EmptyFile
*/
private void download(String filename) throws EmptyFile, NotFound, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError {
initDownloader(filename);
Thread t = new Thread(downLoader);
t.start();
try {
t.join();
if (downLoader.getSuccess()) {
byte[] hash512 = downLoader.getHashSum512();
if (!Arrays.equals(hash512, computeHashsum(filename, HashAlgorithm.SHA512))) {
writeLog("Hashsum does not match", LogLevel.Error);
String line = "Computed checksum:\n";
byte[] c = computeHashsum(filename, HashAlgorithm.SHA512);
for (byte b: c) {
line += String.format("%02X", b);
}
line += "\nReceived checksum:\n";
for (byte b: hash512) {
line += String.format("%02X", b);
}
line += "\n";
writeLog(line, LogLevel.Info);
throw new InternalError();
}
} else {
throw new InternalError();
}
} catch (InterruptedException e) {
throw new InternalError();
}
}
/** Implementation of Runnable
*/
public void run() {
try {
String[] list = listDirectory();
System.out.println("Files present on the server:");
for(String listItem: list) {
System.out.println(listItem);
}
System.out.println("Name of the file to download:");
String f = scanner.nextLine();
download(f);
System.out.println("File " + f + " sucessfully downloaded");
writeLog("File " + f + " sucessfully downloaded", LogLevel.Info);
} catch (EmptyDirectory e) {
writeLog("Server has no file in directory", LogLevel.Error);
} catch (InternalError e) {
writeLog("Client internal error", LogLevel.Error);
} catch (UnknownHostException e) {
writeLog("Server host is unknown", LogLevel.Error);
} catch (IOException e) {
writeLog("Request cannot be send or response cannot be received", LogLevel.Error);
} catch (TransmissionError e) {
writeLog("Message received is too big", LogLevel.Error);
} catch (ProtocolError e) {
writeLog("Cannot decode servers response", LogLevel.Error);
} catch (VersionError e) {
writeLog("Servers response use bad version of the protocol", LogLevel.Error);
} catch (SizeError e) {
writeLog("Cannot handle this packets because of internal representation limitations of numbers on the client", LogLevel.Error);
} catch (InternalRemoteError e) {
writeLog("Server internal error", LogLevel.Error);
} catch (ProtocolRemoteError e) {
writeLog("Server cannot decode clients request", LogLevel.Error);
} catch (VersionRemoteError e) {
writeLog("Server cannot decode this version of the protocol", LogLevel.Error);
} catch (NotFound e) {
writeLog("Server has not this file in directory", LogLevel.Error);
} catch (EmptyFile e) {
writeLog("File is empty", LogLevel.Error);
}
}
}

@ -1,38 +1,15 @@
package clientP2P; package clientP2P;
import exception.InternalError;
import exception.ProtocolError;
import exception.SizeError;
import exception.TransmissionError;
import exception.VersionError;
import remoteException.EmptyFile;
import remoteException.EmptyDirectory;
import remoteException.InternalRemoteError;
import remoteException.NotFound;
import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import java.net.UnknownHostException;
import java.util.Scanner; import java.util.Scanner;
import java.io.IOException;
import java.nio.file.Files;
import java.io.File;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.List;
import tools.HostItem;
import protocolP2P.ProtocolP2PPacketTCP;
import protocolP2P.Payload; import protocolP2P.Payload;
import protocolP2P.RequestResponseCode; import protocolP2P.ProtocolP2PPacket;
import protocolP2P.FileList; import protocolP2P.ProtocolP2PPacketTCP;
import protocolP2P.FilePart; import tools.HostItem;
import protocolP2P.LoadRequest; import tools.Logger;
import protocolP2P.HashAlgorithm; import tools.LogLevel;
import protocolP2P.HashRequest;
import protocolP2P.HashResponse;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import clientP2P.ClientDownloadTCP; import clientP2P.ClientDownloadTCP;
import exception.SocketClosed; import clientP2P.ClientManagement;
/** Implementation of P2P-JAVA-PROJECT CLIENT /** Implementation of P2P-JAVA-PROJECT CLIENT
* @author Louis Royer * @author Louis Royer
@ -40,160 +17,60 @@ import exception.SocketClosed;
* @author JS Auge * @author JS Auge
* @version 1.0 * @version 1.0
*/ */
public class ClientManagementTCP implements Runnable { public class ClientManagementTCP extends ClientManagement {
private String baseDirectory; /** Constructor for TCP implementation, with baseDirectory, tracker, partsSubdir, logger, and scanner parameters.
private String partsSubdir;
private List<HostItem> hostList;
/** Constructor for TCP implementation, with baseDirectory and TCPPort parameters.
* @param baseDirectory the root directory where files are stored * @param baseDirectory the root directory where files are stored
* @param host hostname of the server * @param tracker Tracker hostItem
* @param TCPPort the server will listen on this port * @param partsSubdir subdirectory to store file parts
* @param logger Loggger
* @param scanner Scanner used to read input
*/
public ClientManagementTCP(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, Scanner scanner) {
super(baseDirectory, tracker, partsSubdir, logger, scanner);
}
/** Initialize downloader
* @param filename Name of the file to download
*/
protected void initDownloader(String filename) {
downLoader = (ClientDownload) new ClientDownloadTCP(filename, hostList, partsSubdir, baseDirectory, logger);
}
/** Implementation of writeLog
* @param text Text to log
* @param logLevel level of logging
*/ */
public ClientManagementTCP(String baseDirectory, List<HostItem> hostList, String partsSubdir) { protected void writeLog(String text, LogLevel logLevel) {
this.baseDirectory = baseDirectory; logger.writeTCP(text, logLevel);
this.hostList = hostList;
this.partsSubdir = partsSubdir;
} }
/** Implementation of Runnable /** Implementation of writeLog
* @param e exception to log
* @param logLevel level of logging
*/ */
public void run() { protected void writeLog(Exception e, LogLevel logLevel) {
try { logger.writeTCP(e, logLevel);
System.out.println("Enter all servers: type \"stop\" when finished");
Scanner scanner = new Scanner(System.in);
String[] list = listDirectory();
System.out.println("Files present on the server:");
for(String listItem: list) {
System.out.println(listItem);
}
System.out.println("Name of the file to download:");
String f = scanner.nextLine();
download(f);
System.out.println("File sucessfully downloaded");
} catch (EmptyDirectory e) {
System.err.println("Error: Server has no file in directory");
} catch (InternalError e) {
System.err.println("Error: Client internal error");
} catch (UnknownHostException e) {
System.err.println("Error: Server host is unknown");
} catch (IOException e) {
System.err.println("Error: Request cannot be send or response cannot be received");
} catch (TransmissionError e) {
System.err.println("Error: Message received is too big");
} catch (ProtocolError e) {
System.err.println("Error: Cannot decode servers response");
} catch (VersionError e) {
System.err.println("Error: Servers response use bad version of the protocol");
} catch (SizeError e) {
System.err.println("Error: Cannot handle this packets because of internal representation limitations of numbers on the client");
} catch (InternalRemoteError e) {
System.err.println("Error: Server internal error");
} catch (ProtocolRemoteError e) {
System.err.println("Error: Server cannot decode clients request");
} catch (VersionRemoteError e) {
System.err.println("Error: Server cannot decode this version of the protocol");
} catch (NotFound e) {
System.err.println("Error: Server has not this file in directory");
} catch (EmptyFile e) {
System.err.println("Error: File is empty");
}
} }
/** Try to download a file /** Create packets
* @param filename name of the file to download * @param payload Payload
* @throws NotFound
* @throws InternalError
* @throws UnknownHostException
* @throws IOException
* @throws TransmissionError
* @throws ProtocolError
* @throws VersionError
* @throws SizeError
* @throws InternalRemoteError
* @throws ProtocolRemoteError
* @throws VersionRemoteError
* @throws EmptyFile
*/ */
private void download(String filename) throws EmptyFile, NotFound, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError { protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
ClientDownloadTCP downLoader = new ClientDownloadTCP(filename, hostList, partsSubdir, baseDirectory); return (ProtocolP2PPacket<T>)new ProtocolP2PPacketTCP<T>(payload);
Thread t = new Thread(downLoader);
t.start();
try {
t.join();
if (downLoader.getSuccess()) {
byte[] hash512 = downLoader.getHashSum512();
if (!Arrays.equals(hash512, computeHashsum(filename, HashAlgorithm.SHA512))) {
System.err.println("Error: Hashsum does not match");
System.err.println("Computed checksum:");
byte[] c = computeHashsum(filename, HashAlgorithm.SHA512);
for (byte b: c) {
System.err.print(String.format("%02X", b));
}
System.err.println("");
System.err.println("Received checksum:");
for (byte b: hash512) {
System.err.print(String.format("%02X", b));
}
System.err.println("");
throw new InternalError();
}
} else {
throw new InternalError();
}
} catch (InterruptedException e) {
throw new InternalError();
}
} }
/** list servers directory content /** Getter for tracker socket
* @return list of files
* @throws InternalError
* @throws UnknowHostException
* @throws IOException
* @throws TransmissionError
* @throws ProtocolError
* @throws VersionError
* @throws SizeError
* @throws EmptyDirectory
* @throws InternalRemoteError
* @throws ProtocolRemoteError
* @throws VersionRemoteError
*/ */
private String[] listDirectory() throws EmptyDirectory, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError { protected Object getTrackerSocket() {
ProtocolP2PPacketTCP d = new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.LIST_REQUEST)); return (Object)tracker.getTCPSocket();
try {
d.sendRequest((Object)hostList.get(0).getTCPSocket());
Payload p = d.receiveResponse().getPayload();
assert p instanceof FileList : "This payload must be instance of Filelist";
if (!(p instanceof FileList)) {
throw new InternalError();
} else {
return ((FileList)p).getFileList();
}
} catch (NotFound e) {
throw new ProtocolError();
} catch (EmptyFile e) {
throw new ProtocolError();
} catch (SocketClosed e){
System.err.println("listDirectory : SocketClosed");
throw new ProtocolError();
}
} }
/** Compute Hashsum of a file. /** Getter for HostItem socket
* @param filename * @param hostItem HostItem
* @return hashsum
*/ */
private byte[] computeHashsum(String filename, HashAlgorithm h) { protected Object getHostItemSocket(HostItem hostItem) {
try { return (Object)hostItem.getTCPSocket();
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
return md.digest(Files.readAllBytes(Paths.get(baseDirectory + filename)));
} catch (NoSuchAlgorithmException e) {
System.out.println("Error: " + h.getName() + " not supported");
} catch (IOException e) {
System.out.println("Error: cannot read " + filename);
}
return new byte[0];
} }
} }

@ -1,38 +1,14 @@
package clientP2P; package clientP2P;
import exception.InternalError;
import exception.ProtocolError;
import exception.SizeError;
import exception.TransmissionError;
import exception.VersionError;
import remoteException.EmptyFile;
import remoteException.EmptyDirectory;
import remoteException.InternalRemoteError;
import remoteException.NotFound;
import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import java.net.UnknownHostException;
import java.util.Scanner; import java.util.Scanner;
import java.net.DatagramSocket;
import java.io.IOException;
import java.nio.file.Files;
import java.io.File;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.List;
import tools.HostItem;
import protocolP2P.ProtocolP2PPacketUDP;
import protocolP2P.Payload; import protocolP2P.Payload;
import protocolP2P.RequestResponseCode; import protocolP2P.ProtocolP2PPacket;
import protocolP2P.FileList; import protocolP2P.ProtocolP2PPacketUDP;
import protocolP2P.FilePart; import tools.HostItem;
import protocolP2P.LoadRequest; import tools.Logger;
import protocolP2P.HashAlgorithm; import tools.LogLevel;
import protocolP2P.HashRequest;
import protocolP2P.HashResponse;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import clientP2P.ClientDownloadUDP; import clientP2P.ClientDownloadUDP;
import clientP2P.ClientManagement;
/** Implementation of P2P-JAVA-PROJECT CLIENT /** Implementation of P2P-JAVA-PROJECT CLIENT
* @author Louis Royer * @author Louis Royer
@ -40,157 +16,65 @@ import clientP2P.ClientDownloadUDP;
* @author JS Auge * @author JS Auge
* @version 1.0 * @version 1.0
*/ */
public class ClientManagementUDP implements Runnable { public class ClientManagementUDP extends ClientManagement {
private String baseDirectory; /** Constructor for UDP implementation, with baseDirectory, tracker, partsSubdir, logger and scanner parameters.
private String partsSubdir;
private List<HostItem> hostList;
/** Constructor for UDP implementation, with baseDirectory and UDPPort parameters.
* @param baseDirectory the root directory where files are stored * @param baseDirectory the root directory where files are stored
* @param host hostname of the server * @param tracker tracker HostItem
* @param UDPPort the server will listen on this port * @param partsSubdir subdirectory to store file parts
* @param logger Loggger
* @param scanner Scanner used to read input
*/
public ClientManagementUDP(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, Scanner scanner) {
super(baseDirectory, tracker, partsSubdir, logger, scanner);
}
/** Initialize downloader
* @param filename Name of the file to download
*/
protected void initDownloader(String filename) {
downLoader = (ClientDownload) new ClientDownloadUDP(filename, hostList, partsSubdir, baseDirectory, logger);
}
/** Implementation of writeLog
* @param text Text to log
* @param logLevel level of logging
*/
protected void writeLog(String text, LogLevel logLevel) {
logger.writeUDP(text, logLevel);
}
/** Implementation of writeLog
* @param e exception to log
* @param logLevel level of logging
*/ */
public ClientManagementUDP(String baseDirectory, List<HostItem> hostList, String partsSubdir) { protected void writeLog(Exception e, LogLevel logLevel) {
this.baseDirectory = baseDirectory; logger.writeUDP(e, logLevel);
this.hostList = hostList;
this.partsSubdir = partsSubdir;
} }
/** Implementation of Runnable /** Create packets
* @param payload Payload
*/ */
public void run() { protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
try { return (ProtocolP2PPacket<T>)new ProtocolP2PPacketUDP<T>(payload);
System.out.println("Enter all servers: type \"stop\" when finished");
Scanner scanner = new Scanner(System.in);
String[] list = listDirectory();
System.out.println("Files present on the server:");
for(String listItem: list) {
System.out.println(listItem);
}
System.out.println("Name of the file to download:");
String f = scanner.nextLine();
download(f);
System.out.println("File sucessfully downloaded");
} catch (EmptyDirectory e) {
System.err.println("Error: Server has no file in directory");
} catch (InternalError e) {
System.err.println("Error: Client internal error");
} catch (UnknownHostException e) {
System.err.println("Error: Server host is unknown");
} catch (IOException e) {
System.err.println("Error: Request cannot be send or response cannot be received");
} catch (TransmissionError e) {
System.err.println("Error: Message received is too big");
} catch (ProtocolError e) {
System.err.println("Error: Cannot decode servers response");
} catch (VersionError e) {
System.err.println("Error: Servers response use bad version of the protocol");
} catch (SizeError e) {
System.err.println("Error: Cannot handle this packets because of internal representation limitations of numbers on the client");
} catch (InternalRemoteError e) {
System.err.println("Error: Server internal error");
} catch (ProtocolRemoteError e) {
System.err.println("Error: Server cannot decode clients request");
} catch (VersionRemoteError e) {
System.err.println("Error: Server cannot decode this version of the protocol");
} catch (NotFound e) {
System.err.println("Error: Server has not this file in directory");
} catch (EmptyFile e) {
System.err.println("Error: File is empty");
}
} }
/** Try to download a file /** Getter for tracker socket
* @param filename name of the file to download
* @throws NotFound
* @throws InternalError
* @throws UnknownHostException
* @throws IOException
* @throws TransmissionError
* @throws ProtocolError
* @throws VersionError
* @throws SizeError
* @throws InternalRemoteError
* @throws ProtocolRemoteError
* @throws VersionRemoteError
* @throws EmptyFile
*/ */
private void download(String filename) throws EmptyFile, NotFound, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError { protected Object getTrackerSocket() {
ClientDownloadUDP downLoader = new ClientDownloadUDP(filename, hostList, partsSubdir, baseDirectory); return (Object)tracker.getUDPSocket();
Thread t = new Thread(downLoader);
t.start();
try {
t.join();
if (downLoader.getSuccess()) {
byte[] hash512 = downLoader.getHashSum512();
if (!Arrays.equals(hash512, computeHashsum(filename, HashAlgorithm.SHA512))) {
System.err.println("Error: Hashsum does not match");
System.err.println("Computed checksum:");
byte[] c = computeHashsum(filename, HashAlgorithm.SHA512);
for (byte b: c) {
System.err.print(String.format("%02X", b));
}
System.err.println("");
System.err.println("Received checksum:");
for (byte b: hash512) {
System.err.print(String.format("%02X", b));
}
System.err.println("");
throw new InternalError();
}
} else {
throw new InternalError();
}
} catch (InterruptedException e) {
throw new InternalError();
}
} }
/** list servers directory content /** Getter for HostItem socket
* @return list of files * @param hostItem HostItem
* @throws InternalError
* @throws UnknowHostException
* @throws IOException
* @throws TransmissionError
* @throws ProtocolError
* @throws VersionError
* @throws SizeError
* @throws EmptyDirectory
* @throws InternalRemoteError
* @throws ProtocolRemoteError
* @throws VersionRemoteError
*/ */
private String[] listDirectory() throws EmptyDirectory, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError { protected Object getHostItemSocket(HostItem hostItem) {
ProtocolP2PPacketUDP d = new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.LIST_REQUEST)); return (Object)hostItem.getUDPSocket();
d.sendRequest((Object)hostList.get(0).getUDPSocket());
try {
Payload p = d.receiveResponse().getPayload();
assert p instanceof FileList : "This payload must be instance of Filelist";
if (!(p instanceof FileList)) {
throw new InternalError();
} else {
return ((FileList)p).getFileList();
}
} catch (NotFound e) {
throw new ProtocolError();
} catch (EmptyFile e) {
throw new ProtocolError();
}
} }
/** Compute Hashsum of a file. /** Close HostItem socket
* @param filename * @param hostItem HostItem
* @return hashsum
*/ */
private byte[] computeHashsum(String filename, HashAlgorithm h) { protected void closeHostItemSocket(HostItem hostItem) {
try { hostItem.closeUDPSocket();
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
return md.digest(Files.readAllBytes(Paths.get(baseDirectory + filename)));
} catch (NoSuchAlgorithmException e) {
System.out.println("Error: " + h.getName() + " not supported");
} catch (IOException e) {
System.out.println("Error: cannot read " + filename);
}
return new byte[0];
} }
} }

@ -1,16 +1,19 @@
package clientP2P; package clientP2P;
import java.util.Scanner;
import java.util.List;
import clientP2P.ClientManagementUDP; import clientP2P.ClientManagementUDP;
import clientP2P.ClientManagementTCP; import clientP2P.ClientManagementTCP;
import serverP2P.ServerManagementUDP; import serverP2P.ServerManagementUDP;
import serverP2P.ServerManagementTCP; import serverP2P.ServerManagementTCP;
import tools.Directories;
import tools.Logger; import tools.Logger;
import tools.LogLevel; import tools.LogLevel;
import tools.Directories; import tools.Directories;
import java.util.Scanner;
import java.util.List;
import tools.HostItem; import tools.HostItem;
import tools.HostList; import tools.ServerPortRange;
import tools.TrackerPortRange;
/** Client + Server implementation. /** Client + Server implementation.
* @author Louis Royer * @author Louis Royer
@ -18,106 +21,181 @@ import tools.HostList;
* @author JS Auge * @author JS Auge
* @version 1.0 * @version 1.0
*/ */
public class ClientP2P { public class ClientP2P {
static private final String subdir = "seeded/"; private String logDir = "logs/";
static private String parts = ".parts"; private String partsDir = ".parts/";
private Logger logger; private Logger loggerServer;
private String host; private Logger loggerClient;
private int port;
private Directories directories; private Directories directories;
private List<HostItem> hostList; private HostItem tracker;
private static final int defaultPort = 20000; private HostItem server;
private Scanner scanner;
/** Initialize logger if directories and logger are null, /** Initialize loggers if directories and logger are null,
* else fail silently. * else fail silently.
*/ */
public void initLogger() { public void initDirectoriesAndLoggers() {
if (directories == null && logger == null) { if (directories == null && loggerServer == null && loggerClient == null) {
directories = new Directories("P2P_JAVA_PROJECT" + port); directories = new Directories("P2P_JAVA_PROJECT_" + server.getPort());
logger = new Logger(directories.getDataHomeDirectory() + "server.log"); directories.createSubdir(logDir);
loggerServer = new Logger(directories.getDataHomeDirectory() + logDir + "server.log");
loggerClient = new Logger(directories.getDataHomeDirectory() + logDir + "client.log");
directories.createSubdir(partsDir);
} }
} }
/** Constructor with portStr as parameter. /** Constructor.
* @param portStr String containing port for server listenning. * @param hostnameServer hostname to bind
* @param portServer port to bind
* @param hostnameTracker hostname of tracker
* @param portTracker port of tracker
*/ */
public ClientP2P(String portStr) { public ClientP2P(String hostnameServer, int portServer, String hostnameTracker, int portTracker) {
try{ scanner = new Scanner(System.in);
port = Integer.valueOf(Integer.parseInt(portStr)); server = new HostItem(hostnameServer, portServer);
} catch (NumberFormatException e){ tracker = new HostItem(hostnameTracker, portTracker);
int oldPort = port; initDirectoriesAndLoggers();
port = defaultPort; System.out.println("Server will listen on port " + portServer + " and serve files from " + directories.getDataHomeDirectory());
initLogger(); directories.askOpenDataHomeDirectory(null, scanner);
System.err.println("Error incorrect port " + oldPort + " using default port " + defaultPort); }
logger.write("incorrect port " + oldPort + " using default port " + defaultPort, LogLevel.Info);
} /** Print cli usage
initLogger(); * @param serverPortRange range of server ports
directories.createSubdir(subdir); * @param trackerPortRange range of tracker ports
directories.createSubdir(parts); */
host = "localhost"; private static void printUsage(ServerPortRange serverPortRange, TrackerPortRange trackerPortRange) {
System.out.println("Server will listen on port " + port + " and serve files from " + directories.getDataHomeDirectory() + subdir); System.out.println("usage :");
directories.askOpenDataHomeDirectory(subdir); System.out.println("\tjava clientP2P.ClientP2P");
System.out.println("Please enter list of servers to use; first one will be used to ask list of files"); System.out.println("or");
System.out.println("java clientP2P.ClientP2P -- " +
"<clientTransportProtocol> " +
"<integratedServerHOSTNAME> <integratedServerPORT> " +
"<trackerHOSTNAME> <trackerPORT> ");
System.out.println("(" + trackerPortRange + " and " + serverPortRange +")");
} }
/** Main program entry point. /** Main program entry point.
* 1rst parameter is optionnal, and is used to * 1rst parameter is optionnal, and is used to
* define port used by the server module to listen. If not provided, default to another port. * define port used by the server module to listen. If not provided, default to another port.
* @param args server listenning port * @param args server listenning port
*/ */
public static void main(String [] args) { public static void main(String [] args) {
ClientP2P c; final String defaultHostname = "localhost";
try { String hostnameServer = "";
c = new ClientP2P(args[1]); int portServer = 0;
} catch (IndexOutOfBoundsException e){ String hostnameTracker = "";
c = new ClientP2P("" + defaultPort); int portTracker = 0;
String protocolClient = "";
Scanner scanner = new Scanner(System.in);
final ServerPortRange serverPortRange = new ServerPortRange();
final TrackerPortRange trackerPortRange = new TrackerPortRange();
if ((args.length != 6) && (args.length != 0)){
ClientP2P.printUsage(serverPortRange, trackerPortRange);
System.exit(1);
}
else if(args.length == 6){
protocolClient = args[1];
hostnameServer = args[2];
portServer = Integer.valueOf(Integer.parseInt(args[3]));
hostnameTracker = args[4];
portTracker = Integer.valueOf(Integer.parseInt(args[5]));
} else{
System.out.println("Client, wich transport protocol do you want to use (default = TCP): ");
protocolClient = scanner.nextLine();
System.out.println("server side, enter hostname to bind (default = localhost): ");
hostnameServer = scanner.nextLine();
if(hostnameServer.equals("")){
hostnameServer = defaultHostname;
System.out.println("using default hostname : " + hostnameServer);
}
System.out.println("enter port (default = " + serverPortRange.getDefaultPort() +"): ");
String portServerStr = scanner.nextLine();
if(portServerStr.equals("")){
portServer = serverPortRange.getDefaultPort();
System.out.println("using default port : " + portServer);
} else {
portServer = Integer.valueOf(Integer.parseInt(portServerStr));
}
System.out.println("enter hostname of tracker (default = localhost): ");
hostnameTracker = scanner.nextLine();
if(hostnameTracker.equals("")){
hostnameTracker = defaultHostname;
System.out.println("tracker default hostname : " + hostnameTracker);
}
System.out.println("enter tracker's port (default = "+trackerPortRange.getDefaultPort() + "): ");
String portTrackerStr = scanner.nextLine();
if(portTrackerStr.equals("")){
portTracker = trackerPortRange.getDefaultPort();
System.out.println("using default port : " + portTracker);
} else {
portTracker = Integer.valueOf(Integer.parseInt(portTrackerStr));
}
}
System.out.println("using hostname : " + hostnameServer);
if(serverPortRange.isPortInRange(portServer)){
System.out.println("using port : " + portServer);
}
else {
System.out.println("Port not in range. " + serverPortRange);
portServer = serverPortRange.getDefaultPort();
} }
System.out.println("tracker hostname : " + hostnameTracker);
// Server threads if(trackerPortRange.isPortInRange(portTracker)){
ServerManagementUDP smudp = new ServerManagementUDP(c.directories.getDataHomeDirectory() + subdir, c.port, c.logger); System.out.println("using port : " + portTracker);
ServerManagementTCP smtcp = new ServerManagementTCP(c.directories.getDataHomeDirectory() + subdir, c.port, c.logger); }
else {
System.out.println("Port not in range. " + trackerPortRange);
portTracker = trackerPortRange.getDefaultPort();
}
ClientP2P c = new ClientP2P(hostnameServer, portServer, hostnameTracker, portTracker);
ServerManagementUDP smudp = new ServerManagementUDP(c.directories.getDataHomeDirectory(), c.server, c.tracker, c.loggerServer);
ServerManagementTCP smtcp = new ServerManagementTCP(c.directories.getDataHomeDirectory(), c.server, c.tracker, c.loggerServer);
Thread tudp = new Thread(smudp); Thread tudp = new Thread(smudp);
tudp.setName("server UDP P2P-JAVA-PROJECT (port: " + c.port + ")"); tudp.setName("server UDP P2P-JAVA-PROJECT");
tudp.start(); tudp.start();
Thread ttcp = new Thread(smtcp); Thread ttcp = new Thread(smtcp);
ttcp.setName("server TCP P2P-JAVA-PROJECT (port: " + c.port + ")"); ttcp.setName("server TCP P2P-JAVA-PROJECT");
ttcp.start(); ttcp.start();
// Wait a bit before printing client interface // Wait a bit before printing client interface
// This is not required, but allow to have a cleaner interface // This is not required, but allow to have a cleaner interface
try { try {
Thread.sleep(100); Thread.sleep(200);
} catch(InterruptedException e) { } catch(InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
} }
// initialize Host lists Thread tclient;
c.hostList = HostList.getServList(); switch(protocolClient){
System.out.println("Client : Which transport protocol do you want to use? [TCP/udp]");
Scanner sc = new Scanner(System.in);
String transportchoosen = sc.nextLine();
Thread t;
switch(transportchoosen){
case "UDP": case "UDP":
case "udp": case "udp":
case "upd": // alias typo case "upd": // to avoid users typos
case "2" : case "2" :
System.out.println("Starting with UDP"); System.out.println("Starting with UDP");
ClientManagementUDP cmudp = new ClientManagementUDP(c.directories.getDataHomeDirectory(), c.hostList, c.directories.getDataHomeDirectory() + c.parts + "/"); ClientManagementUDP cmudp = new ClientManagementUDP(c.directories.getDataHomeDirectory(), c.tracker, c.directories.getDataHomeDirectory() + c.partsDir, c.loggerClient, c.scanner);
t = new Thread(cmudp); tclient = new Thread(cmudp);
break; break;
case "TCP": case "TCP":
case "tcp": case "tcp":
case "1": case "1":
default: default:
System.out.println("Starting with TCP"); System.out.println("Starting with TCP");
ClientManagementTCP cmtcp = new ClientManagementTCP(c.directories.getDataHomeDirectory(), c.hostList, c.directories.getDataHomeDirectory() + c.parts + "/"); ClientManagementTCP cmtcp = new ClientManagementTCP(c.directories.getDataHomeDirectory(), c.tracker, c.directories.getDataHomeDirectory() + c.partsDir, c.loggerClient, c.scanner);
t = new Thread(cmtcp); tclient = new Thread(cmtcp);
break; break;
} }
tclient.setName("client P2P-JAVA-PROJECT");
t.setName("client P2P-JAVA-PROJECT"); tclient.start();
t.start(); try {
tclient.join();
} catch (InterruptedException e) {}
smudp.setStop();
smtcp.setStop();
} }
} }

@ -1,4 +1,5 @@
package exception; package exception;
public class InternalError extends Exception {
public abstract class LocalException extends Exception {
private static final long serialVersionUID = 12L; private static final long serialVersionUID = 12L;
} }

@ -1,4 +1,5 @@
package exception; package exception;
public class ProtocolError extends Exception {
public abstract class RemoteException extends Exception {
private static final long serialVersionUID = 12L; private static final long serialVersionUID = 12L;
} }

@ -1,4 +0,0 @@
package exception;
public class SocketClosed extends Exception {
private static final long serialVersionUID = 12L;
}

@ -1,4 +0,0 @@
package exception;
public class TransmissionError extends Exception {
private static final long serialVersionUID = 12L;
}

@ -1,4 +0,0 @@
package exception;
public class VersionError extends Exception {
private static final long serialVersionUID = 12L;
}

@ -0,0 +1,7 @@
package localException;
import exception.LocalException;
public class InternalError extends LocalException {
private static final long serialVersionUID = 12L;
}

@ -0,0 +1,7 @@
package localException;
import exception.LocalException;
public class ProtocolError extends LocalException {
private static final long serialVersionUID = 12L;
}

@ -1,5 +1,9 @@
package exception; package localException;
import exception.LocalException;
/** Used on reception side when size as set in Packet is too big, and we cant store this in a int/long as usual. */ /** Used on reception side when size as set in Packet is too big, and we cant store this in a int/long as usual. */
public class SizeError extends Exception { public class SizeError extends LocalException {
private static final long serialVersionUID = 12L; private static final long serialVersionUID = 12L;
} }

@ -0,0 +1,7 @@
package localException;
import exception.LocalException;
public class SocketClosed extends LocalException {
private static final long serialVersionUID = 12L;
}

@ -0,0 +1,7 @@
package localException;
import exception.LocalException;
public class TransmissionError extends LocalException {
private static final long serialVersionUID = 12L;
}

@ -0,0 +1,7 @@
package localException;
import exception.LocalException;
public class VersionError extends LocalException {
private static final long serialVersionUID = 12L;
}

@ -8,6 +8,7 @@ package protocolP2P;
*/ */
public enum CodeType { public enum CodeType {
REQUEST, REQUEST,
REQUEST_TRACKER,
RESPONSE, RESPONSE,
ERROR ERROR
} }

@ -0,0 +1,71 @@
package protocolP2P;
import protocolP2P.Payload;
import tools.BytesArrayTools;
import localException.InternalError;
import localException.SizeError;
import localException.ProtocolError;
import localException.TransmissionError;
/** Representation of payload for discover request.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class DiscoverRequest extends Payload {
private String filename;
/** Constructor with filename (typically used by client). If filename is null, it is initialized with "".
* @param filename Name of the file you want a server list of.
* @throws InternalError
*/
public DiscoverRequest(String filename) throws InternalError {
super(RequestResponseCode.DISCOVER_REQUEST);
if (filename == null) {
this.filename = "";
} else {
this.filename = filename;
}
}
/** Constructor (typically used by server) with a byte[] parameter containing the Packet received.
* @param packet the full Packet received
* @throws SizeError
* @throws InternalError
* @throws ProtocolError
* @throws TransmissionError
*/
protected DiscoverRequest(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
super(packet);
int size = getPayloadSize(packet);
filename = BytesArrayTools.readString(packet, Payload.PAYLOAD_START_POSITION, size);
}
/** Returns a byte[] containing Packet with padding.
* This Packet is still incomplete and should not be send directly.
* ProtocolP2PPacket will use this method to generate the complete Packet.
* @return Packet with padding
* @throws InternalError
*/
protected byte[] toPacket() throws InternalError {
// compute total size
int size = PAYLOAD_START_POSITION + filename.length();
byte[] packet = new byte[size + 1]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size
setPayloadSize(size - PAYLOAD_START_POSITION, packet);
// write filename to Packet
BytesArrayTools.write(packet, filename, PAYLOAD_START_POSITION);
return packet;
}
/** Filename getter.
* @return filename
*/
public String getFilename() {
return filename;
}
}

@ -0,0 +1,117 @@
package protocolP2P;
import protocolP2P.Payload;
import tools.HostItem;
import java.util.ArrayList;
import java.util.List;
import localException.InternalError;
import localException.SizeError;
import localException.ProtocolError;
import localException.TransmissionError;
import tools.BytesArrayTools;
/** Representation of payload for discover response.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class DiscoverResponse extends Payload {
private List<HostItem> hostList;
private String filename;
private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION;
private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
/** Constructor with filename (typically used by tracker). If filename is null, it is initialized with "".
* @param filename Name of the file related to the server list.
* @param hostList List of servers
* @throws InternalError
*/
public DiscoverResponse(String filename, List<HostItem> hostList) throws InternalError {
super(RequestResponseCode.DISCOVER_RESPONSE);
this.filename = filename;
this.hostList = hostList;
}
/** Constructor (typically used by server) with a byte[] parameter containing the Packet received.
* @param packet the full Packet received
* @throws SizeError
* @throws InternalError
* @throws ProtocolError
* @throws TransmissionError
*/
protected DiscoverResponse(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
super(packet);
int size = getPayloadSize(packet);
/* Read filename size */
int filenameSize = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION);
/* Read filename */
filename = BytesArrayTools.readString(packet, FILENAME_POSITION, filenameSize);
// TODO
hostList = new ArrayList<>();
int i = FILENAME_POSITION + filenameSize;
while(i<size) {
int port = BytesArrayTools.readInt16Bits(packet, i);
i += 2;
String hostname = BytesArrayTools.readString(packet, i, "\n");
i += hostname.length();
hostList.add(new HostItem(hostname, port));
}
}
/** Returns a byte[] containing Packet with padding.
* This Packet is still incomplete and should not be send directly.
* ProtocolP2PPacket will use this method to generate the complete Packet.
* @return Packet with padding
* @throws InternalError
*/
protected byte[] toPacket() throws InternalError {
int filenameSize = filename.length();
int hostListSize = 0;
for (HostItem hostItem: hostList) {
hostListSize += (2 + hostItem.getHostname().length() + 1);
}
// compute total size
int size = FILENAME_POSITION + filename.length() + hostListSize;
byte[] packet = new byte[size + 1]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size
setPayloadSize(size - PAYLOAD_START_POSITION, packet);
// write filename size
BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filenameSize);
// write filename
BytesArrayTools.write(packet, filename, FILENAME_POSITION);
int i = FILENAME_POSITION + filename.length();
// write hostList
for(HostItem hostItem: hostList) {
try {
BytesArrayTools.write16Bits(packet, i, hostItem.getPort());
i+=2;
String hostname = hostItem.getHostname() + "\n";
BytesArrayTools.write(packet, hostname, i);
i+=hostname.length();
} catch (SizeError e) {
throw new InternalError();
}
}
return packet;
}
/** HostList getter.
* @return hostList
*/
public List<HostItem> getHostList() {
return hostList;
}
/** Filename getter.
* @return filename
*/
public String getFilename() {
return filename;
}
}

@ -1,12 +1,10 @@
package protocolP2P; package protocolP2P;
import java.util.Arrays;
import protocolP2P.Payload; import protocolP2P.Payload;
import protocolP2P.RequestResponseCode; import protocolP2P.RequestResponseCode;
import exception.TransmissionError; import localException.TransmissionError;
import exception.ProtocolError; import localException.ProtocolError;
import exception.InternalError; import localException.InternalError;
import exception.SizeError; import localException.SizeError;
import java.io.UnsupportedEncodingException;
import tools.BytesArrayTools; import tools.BytesArrayTools;
/** Representation of payload for list response. /** Representation of payload for list response.

@ -1,13 +1,11 @@
package protocolP2P; package protocolP2P;
import protocolP2P.Payload; import protocolP2P.Payload;
import protocolP2P.RequestResponseCode; import protocolP2P.RequestResponseCode;
import exception.ProtocolError; import localException.ProtocolError;
import exception.InternalError; import localException.InternalError;
import exception.SizeError; import localException.SizeError;
import exception.TransmissionError; import localException.TransmissionError;
import tools.BytesArrayTools; import tools.BytesArrayTools;
import java.util.Arrays;
import java.io.UnsupportedEncodingException;
/** Representation of payload for load response. /** Representation of payload for load response.
* @author Louis Royer * @author Louis Royer
@ -36,7 +34,7 @@ public class FilePart extends Payload {
super(RequestResponseCode.LOAD_RESPONSE); super(RequestResponseCode.LOAD_RESPONSE);
/* asserts to help debugging */ /* asserts to help debugging */
assert totalSize >= 0 : "totalSize cannot be negative"; assert totalSize >= 0 : "totalSize cannot be negative";
assert partialContent.length != 0 : "partialContent.length cannot be zero, see RRCode.EMPTY_FILE"; assert partialContent.length != 0 : "partialContent.length cannot be zero, see RRCode.EMPTY_FILE";
assert totalSize >= partialContent.length : "totalSize must be greater than partialContent.length"; assert totalSize >= partialContent.length : "totalSize must be greater than partialContent.length";
assert offset >= 0 : "offset cannot be negative"; assert offset >= 0 : "offset cannot be negative";
assert filename != null : "filename is required"; assert filename != null : "filename is required";
@ -83,7 +81,7 @@ public class FilePart extends Payload {
// set request/response code // set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue; packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size // set Payload size
setPayloadSize(size - OFFSET_POSITION, packet); setPayloadSize(size - PAYLOAD_START_POSITION, packet);
// write offset to Packet // write offset to Packet
BytesArrayTools.write(packet, OFFSET_POSITION, offset); BytesArrayTools.write(packet, OFFSET_POSITION, offset);
// write totalSize to Packet // write totalSize to Packet

@ -1,11 +1,10 @@
package protocolP2P; package protocolP2P;
import protocolP2P.Payload; import protocolP2P.Payload;
import protocolP2P.HashAlgorithm; import protocolP2P.HashAlgorithm;
import java.io.UnsupportedEncodingException; import localException.TransmissionError;
import exception.TransmissionError; import localException.SizeError;
import exception.SizeError; import localException.ProtocolError;
import exception.ProtocolError; import localException.InternalError;
import exception.InternalError;
import tools.BytesArrayTools; import tools.BytesArrayTools;
@ -21,12 +20,12 @@ public class HashRequest extends Payload {
private HashAlgorithm[] algoList; private HashAlgorithm[] algoList;
private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION; private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION;
private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4; private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
/** Constructor (typically used by the server) with a filename parameter. /** Constructor (typically used by the server) with a filename parameter.
* @param filename name of the file to download. Must not be empty. * @param filename name of the file to download. Must not be empty.
* @param algoList List of hash algorithms used * @param algoList List of hash algorithms used
* @throws InternalError * @throws InternalError
* *
*/ */
public HashRequest(String filename, HashAlgorithm[] algoList) throws InternalError { public HashRequest(String filename, HashAlgorithm[] algoList) throws InternalError {
super(RequestResponseCode.HASH_REQUEST); super(RequestResponseCode.HASH_REQUEST);

@ -2,11 +2,10 @@ package protocolP2P;
import protocolP2P.Payload; import protocolP2P.Payload;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.io.UnsupportedEncodingException; import localException.TransmissionError;
import exception.TransmissionError; import localException.SizeError;
import exception.SizeError; import localException.ProtocolError;
import exception.ProtocolError; import localException.InternalError;
import exception.InternalError;
import tools.BytesArrayTools; import tools.BytesArrayTools;
@ -22,12 +21,12 @@ public class HashResponse extends Payload {
private Map<HashAlgorithm, byte[]> hashes = new HashMap<>(); private Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION; private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION;
private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4; private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
/** Constructor (typically used by the server) with a filename parameter. /** Constructor (typically used by the server) with a filename parameter.
* @param filename name of the file to download. Must not be empty. * @param filename name of the file to download. Must not be empty.
* @param hashes HashMap containing hashes for file. * @param hashes HashMap containing hashes for file.
* @throws InternalError * @throws InternalError
* *
*/ */
public HashResponse(String filename, Map<HashAlgorithm, byte[]> hashes) throws InternalError { public HashResponse(String filename, Map<HashAlgorithm, byte[]> hashes) throws InternalError {
super(RequestResponseCode.HASH_RESPONSE); super(RequestResponseCode.HASH_RESPONSE);
@ -80,7 +79,7 @@ public class HashResponse extends Payload {
} }
start += hashSize; start += hashSize;
} while (start < size); } while (start < size);
} }
/** Returns a byte[] containing Packet with padding. /** Returns a byte[] containing Packet with padding.
@ -100,7 +99,7 @@ public class HashResponse extends Payload {
size += 4 + s.length; size += 4 + s.length;
} }
byte[] packet = new byte[size + 1]; // java initialize all to zero byte[] packet = new byte[size + 1]; // java initialize all to zero
// set request/response code // set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue; packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size // set Payload size
@ -112,14 +111,14 @@ public class HashResponse extends Payload {
int bCount = FILENAME_POSITION + filename.length(); int bCount = FILENAME_POSITION + filename.length();
for(HashAlgorithm h : hashes.keySet()) { for(HashAlgorithm h : hashes.keySet()) {
String s = h.getName(); String s = h.getName();
BytesArrayTools.write(packet, bCount, (int)s.length()); BytesArrayTools.write(packet, bCount, s.length());
bCount += 4; bCount += 4;
// write algoname // write algoname
BytesArrayTools.write(packet, s, bCount); BytesArrayTools.write(packet, s, bCount);
bCount += s.length(); bCount += s.length();
// write hash size // write hash size
byte[] hashb = hashes.get(HashAlgorithm.fromName(s)); byte[] hashb = hashes.get(HashAlgorithm.fromName(s));
BytesArrayTools.write(packet, bCount, (int)hashb.length); BytesArrayTools.write(packet, bCount, hashb.length);
bCount += 4; bCount += 4;
if (hashb.length != 0) { if (hashb.length != 0) {
// write hash // write hash

@ -1,12 +1,11 @@
package protocolP2P; package protocolP2P;
import protocolP2P.Payload; import protocolP2P.Payload;
import protocolP2P.RequestResponseCode; import protocolP2P.RequestResponseCode;
import exception.TransmissionError; import localException.TransmissionError;
import exception.ProtocolError; import localException.ProtocolError;
import exception.InternalError; import localException.InternalError;
import exception.SizeError; import localException.SizeError;
import tools.BytesArrayTools; import tools.BytesArrayTools;
import java.io.UnsupportedEncodingException;
/** Representation of payload for load request. /** Representation of payload for load request.
* @author Louis Royer * @author Louis Royer
@ -61,7 +60,7 @@ public class LoadRequest extends Payload {
/* Read maxSizePartialContent */ /* Read maxSizePartialContent */
maxSizePartialContent = BytesArrayTools.readLong(packet, MAX_SIZE_PARTIAL_CONTENT_POSITION); maxSizePartialContent = BytesArrayTools.readLong(packet, MAX_SIZE_PARTIAL_CONTENT_POSITION);
/* Read filename */ /* Read filename */
int size = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION); int size = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION);
filename = BytesArrayTools.readString(packet, FILENAME_POSITION, size); filename = BytesArrayTools.readString(packet, FILENAME_POSITION, size);
@ -83,11 +82,11 @@ public class LoadRequest extends Payload {
// set Payload size // set Payload size
setPayloadSize(size - OFFSET_POSITION, packet); setPayloadSize(size - OFFSET_POSITION, packet);
// Write offset // Write offset
BytesArrayTools.write(packet, OFFSET_POSITION, (long)offset); BytesArrayTools.write(packet, OFFSET_POSITION, offset);
// Write maxSizePartialContent // Write maxSizePartialContent
BytesArrayTools.write(packet, MAX_SIZE_PARTIAL_CONTENT_POSITION, (long)maxSizePartialContent); BytesArrayTools.write(packet, MAX_SIZE_PARTIAL_CONTENT_POSITION, maxSizePartialContent);
// Write filenameSize // Write filenameSize
BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, (int)filenameSize); BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filenameSize);
// Write filename // Write filename
BytesArrayTools.write(packet, filename, FILENAME_POSITION); BytesArrayTools.write(packet, filename, FILENAME_POSITION);
return packet; return packet;

@ -5,10 +5,14 @@ import protocolP2P.FileList;
import protocolP2P.LoadRequest; import protocolP2P.LoadRequest;
import protocolP2P.HashRequest; import protocolP2P.HashRequest;
import protocolP2P.HashResponse; import protocolP2P.HashResponse;
import exception.ProtocolError; import protocolP2P.DiscoverRequest;
import exception.InternalError; import protocolP2P.DiscoverResponse;
import exception.TransmissionError; import protocolP2P.Register;
import exception.SizeError; import protocolP2P.Unregister;
import localException.ProtocolError;
import localException.InternalError;
import localException.TransmissionError;
import localException.SizeError;
import tools.BytesArrayTools; import tools.BytesArrayTools;
/** Representation of payload. If payload has a size, use subclasses instead. /** Representation of payload. If payload has a size, use subclasses instead.
* @author Louis Royer * @author Louis Royer
@ -27,11 +31,15 @@ public class Payload {
*/ */
public Payload(RequestResponseCode requestResponseCode) throws InternalError { public Payload(RequestResponseCode requestResponseCode) throws InternalError {
/* asserts to help debugging */ /* asserts to help debugging */
assert requestResponseCode != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class"; assert requestResponseCode != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class";
assert requestResponseCode != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class"; assert requestResponseCode != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class";
assert requestResponseCode != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class"; assert requestResponseCode != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class";
assert requestResponseCode != RequestResponseCode.HASH_REQUEST || (this instanceof HashRequest) : "HASH_REQUEST must use HashRequest class"; assert requestResponseCode != RequestResponseCode.HASH_REQUEST || (this instanceof HashRequest) : "HASH_REQUEST must use HashRequest class";
assert requestResponseCode != RequestResponseCode.HASH_RESPONSE || (this instanceof HashResponse) : "HASH_RESPONSE must use HashResponse class"; assert requestResponseCode != RequestResponseCode.HASH_RESPONSE || (this instanceof HashResponse) : "HASH_RESPONSE must use HashResponse class";
assert requestResponseCode != RequestResponseCode.DISCOVER_REQUEST || (this instanceof DiscoverRequest) : "DISCOVER_REQUEST must use DiscoverRequest class";
assert requestResponseCode != RequestResponseCode.DISCOVER_RESPONSE || (this instanceof DiscoverResponse) : "DISCOVER_RESPONSE must use DiscoverResponse class";
assert requestResponseCode != RequestResponseCode.REGISTER || (this instanceof Register) : "REGISTER must use Register class";
assert requestResponseCode != RequestResponseCode.UNREGISTER || (this instanceof Unregister) : "UNREGISTER must use Unregister class";
this.requestResponseCode = requestResponseCode; this.requestResponseCode = requestResponseCode;
checkRequestResponseCode(); // this can throw InternalError checkRequestResponseCode(); // this can throw InternalError
} }
@ -46,29 +54,42 @@ public class Payload {
*/ */
protected Payload(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError { protected Payload(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
/* asserts to help debugging */ /* asserts to help debugging */
assert getPayloadSize(packet) + 8 <= packet.length : "Payload is truncated"; assert getPayloadSize(packet) + PAYLOAD_START_POSITION <= packet.length : "Payload is truncated";
if (packet.length < getPayloadSize(packet) + 8) { if (packet.length < getPayloadSize(packet) + PAYLOAD_START_POSITION) {
throw new TransmissionError(); throw new TransmissionError();
} }
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class"; assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class"; assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class"; assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.HASH_REQUEST || (this instanceof HashRequest) : "HASH_REQUEST must use HashRequest class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.HASH_RESPONSE || (this instanceof HashResponse) : "HASH_RESPONSE must use HashResponse class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.DISCOVER_REQUEST || (this instanceof DiscoverRequest) : "DISCOVER_REQUEST must use DiscoverRequest class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.DISCOVER_RESPONSE || (this instanceof DiscoverResponse) : "DISCOVER_RESPONSE must use DiscoverResponse class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.REGISTER || (this instanceof Register) : "REGISTER must use Register class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.UNREGISTER || (this instanceof Unregister) : "UNREGISTER must use Unregister class";
requestResponseCode = RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]); requestResponseCode = RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]);
checkRequestResponseCode(); // this can throw InternalError checkRequestResponseCode(); // this can throw InternalError
} }
/** Used to check RRCode used is compatible with this class use, or if a more specific subclass is required. /** Used to check RRCode used is compatible with this class use, or if a more specific subclass is required.
* @throws InternalError * @throws InternalError
*/ */
private void checkRequestResponseCode() throws InternalError { private void checkRequestResponseCode() throws InternalError {
/* Incorrect use cases (use subclasses instead) */ /* Incorrect use cases (use subclasses instead) */
if ((requestResponseCode == RequestResponseCode.LIST_RESPONSE && !(this instanceof FileList)) if ((requestResponseCode == RequestResponseCode.LIST_RESPONSE && !(this instanceof FileList))
|| (requestResponseCode == RequestResponseCode.LOAD_RESPONSE && !(this instanceof FilePart)) || (requestResponseCode == RequestResponseCode.LOAD_RESPONSE && !(this instanceof FilePart))
|| (requestResponseCode == RequestResponseCode.LOAD_REQUEST && !(this instanceof LoadRequest))) { || (requestResponseCode == RequestResponseCode.LOAD_REQUEST && !(this instanceof LoadRequest))
|| (requestResponseCode == RequestResponseCode.HASH_REQUEST && !(this instanceof HashRequest))
|| (requestResponseCode == RequestResponseCode.HASH_RESPONSE && !(this instanceof HashResponse))
|| (requestResponseCode == RequestResponseCode.DISCOVER_REQUEST && !(this instanceof DiscoverRequest))
|| (requestResponseCode == RequestResponseCode.DISCOVER_RESPONSE && !(this instanceof DiscoverResponse))
|| (requestResponseCode == RequestResponseCode.REGISTER && !(this instanceof Register))
|| (requestResponseCode == RequestResponseCode.UNREGISTER && !(this instanceof Unregister))
) {
throw new InternalError(); throw new InternalError();
} }
} }
/** Returns a byte[] containing Packet with padding. /** Returns a byte[] containing Packet with padding.
* This Packet is still incomplete and should not be send directly. * This Packet is still incomplete and should not be send directly.
* ProtocolP2PPacket will use this method to generate the complete Packet. * ProtocolP2PPacket will use this method to generate the complete Packet.
@ -90,7 +111,7 @@ public class Payload {
* @param size integer representing payload size * @param size integer representing payload size
* @param packet Packet to be completed * @param packet Packet to be completed
* @throws InternalError * @throws InternalError
*/ */
protected static void setPayloadSize(int size, byte[] packet) throws InternalError { protected static void setPayloadSize(int size, byte[] packet) throws InternalError {
/* assert to help debugging */ /* assert to help debugging */
assert size >= 0: "Payload size cannot be negative"; assert size >= 0: "Payload size cannot be negative";

@ -1,18 +1,19 @@
package protocolP2P; package protocolP2P;
import exception.InternalError; import localException.InternalError;
import exception.ProtocolError; import localException.ProtocolError;
import exception.SizeError; import localException.SizeError;
import exception.TransmissionError; import localException.TransmissionError;
import exception.VersionError; import localException.VersionError;
import exception.SocketClosed; import localException.SocketClosed;
import remoteException.EmptyDirectory; import remoteException.EmptyDirectory;
import remoteException.InternalRemoteError; import remoteException.InternalRemoteError;
import remoteException.NotFound; import remoteException.NotFound;
import remoteException.ProtocolRemoteError; import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError; import remoteException.VersionRemoteError;
import remoteException.EmptyFile; import remoteException.EmptyFile;
import java.net.InetAddress; import remoteException.NotATracker;
import java.io.IOException; import java.io.IOException;
import tools.HostItem;
/** Representation of packet. /** Representation of packet.
* @author Louis Royer * @author Louis Royer
@ -20,7 +21,7 @@ import java.io.IOException;
* @author JS Auge * @author JS Auge
* @version 1.0 * @version 1.0
*/ */
public abstract class ProtocolP2PPacket { public abstract class ProtocolP2PPacket < T extends Payload>{
private final static byte PROTOCOL_VERSION = 0x12; private final static byte PROTOCOL_VERSION = 0x12;
protected final static int VERSION_POSITION = 0; protected final static int VERSION_POSITION = 0;
protected byte version; protected byte version;
@ -29,9 +30,9 @@ public abstract class ProtocolP2PPacket {
/** Constructor with payload parameter (typically used when sending Packet). /** Constructor with payload parameter (typically used when sending Packet).
* @param payload the payload associated with the Packet to send * @param payload the payload associated with the Packet to send
*/ */
public ProtocolP2PPacket(Payload payload) { public ProtocolP2PPacket (T payload) {
version = PROTOCOL_VERSION; version = PROTOCOL_VERSION;
this.payload = payload; this.payload = (Payload)payload;
} }
/** Send a request /** Send a request
@ -48,11 +49,18 @@ public abstract class ProtocolP2PPacket {
* @throws IOException * @throws IOException
* @throws SocketClosed * @throws SocketClosed
*/ */
public abstract void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException, SocketClosed; public abstract <U extends ProtocolP2PPacket<?>> void sendResponse(U response) throws InternalError, IOException, SocketClosed;
/** Get hostItem of the sender
* @return hostItem of the sender
* @throws InternalError
*/
public abstract HostItem getHostItem() throws InternalError;
/** Receive a response /** Receive a response
* @throws EmptyFile * @throws EmptyFile
* @throws NotFound * @throws NotFound
* @throws NotATracker
* @throws EmptyDirectory * @throws EmptyDirectory
* @throws InternalRemoteError * @throws InternalRemoteError
* @throws VersionRemoteError * @throws VersionRemoteError
@ -65,7 +73,7 @@ public abstract class ProtocolP2PPacket {
* @throws IOException * @throws IOException
* @throws SocketClosed * @throws SocketClosed
*/ */
public abstract ProtocolP2PPacket receiveResponse() throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, SocketClosed; public abstract ProtocolP2PPacket<?> receiveResponse() throws EmptyFile, NotFound, NotATracker, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, SocketClosed;
/** Receive a request, subclasses must overwrite this constructor. /** Receive a request, subclasses must overwrite this constructor.
* @param socket socket used to get the request * @param socket socket used to get the request
@ -88,7 +96,7 @@ public abstract class ProtocolP2PPacket {
* @throws SizeError * @throws SizeError
*/ */
protected ProtocolP2PPacket(byte[] packet) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError {} protected ProtocolP2PPacket(byte[] packet) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError {}
/** Returns Payload associated with the Packet. /** Returns Payload associated with the Packet.
* @return payload associated with the Packet. * @return payload associated with the Packet.
*/ */
@ -101,6 +109,7 @@ public abstract class ProtocolP2PPacket {
*/ */
protected void checkProtocolVersion() throws VersionError { protected void checkProtocolVersion() throws VersionError {
if (PROTOCOL_VERSION != version) { if (PROTOCOL_VERSION != version) {
System.err.println("Error: wrong version in packet:" + version);
throw new VersionError(); throw new VersionError();
} }
} }

@ -1,24 +1,23 @@
package protocolP2P; package protocolP2P;
import exception.InternalError; import localException.InternalError;
import exception.ProtocolError; import localException.ProtocolError;
import exception.SizeError; import localException.SizeError;
import exception.TransmissionError; import localException.TransmissionError;
import exception.VersionError; import localException.VersionError;
import exception.SocketClosed; import localException.SocketClosed;
import remoteException.NotATracker;
import remoteException.EmptyDirectory; import remoteException.EmptyDirectory;
import remoteException.InternalRemoteError; import remoteException.InternalRemoteError;
import remoteException.NotFound; import remoteException.NotFound;
import remoteException.ProtocolRemoteError; import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError; import remoteException.VersionRemoteError;
import remoteException.EmptyFile; import remoteException.EmptyFile;
import tools.BytesArrayTools; import tools.HostItem;
import protocolP2P.Payload; import protocolP2P.Payload;
import protocolP2P.RequestResponseCode; import protocolP2P.RequestResponseCode;
import protocolP2P.LoadRequest; import protocolP2P.LoadRequest;
import protocolP2P.FileList; import protocolP2P.FileList;
import protocolP2P.FilePart; import protocolP2P.FilePart;
import java.util.ArrayList;
import java.lang.Byte;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.net.Socket; import java.net.Socket;
@ -29,15 +28,15 @@ import java.net.Socket;
* @author JS Auge * @author JS Auge
* @version 1.0 * @version 1.0
*/ */
public class ProtocolP2PPacketTCP extends ProtocolP2PPacket { public class ProtocolP2PPacketTCP < T extends Payload > extends ProtocolP2PPacket < T > {
private Socket responseSocket; // socket used to recept request and send response private Socket responseSocket; // socket used to recept request and send response
private Socket requestSocket; // socket used to send request and to reception response private Socket requestSocket; // socket used to send request and to reception response
/** Constructor with payload parameter (typically used when sending packet). /** Constructor with payload parameter (typically used when sending packet).
* @param payload the payload associated with the packet to send * @param payload the payload associated with the packet to send
*/ */
public ProtocolP2PPacketTCP(Payload payload) { public ProtocolP2PPacketTCP(T payload) {
super(payload); super(payload);
} }
@ -67,9 +66,9 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
socket.close(); socket.close();
} catch (IOException e2) { } catch (IOException e2) {
System.err.println("Cannot close socket"); System.err.println("Cannot close socket");
} finally {
throw new SocketClosed(); throw new SocketClosed();
} }
} }
} }
@ -109,14 +108,16 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
Socket ss = (Socket)socket; Socket ss = (Socket)socket;
byte[] packet = new byte[1024]; byte[] packet = new byte[1024];
try { try {
System.err.println("Reading " + ss.getInputStream().read(packet) + " bytes"); if (-1 == ss.getInputStream().read(packet)) {
throw new IOException();
}
} catch (IOException e) { } catch (IOException e) {
// Error: cannot read request, closing socket // Error: cannot read request, closing socket
try { try {
ss.close(); ss.close();
throw new SocketClosed();
} catch (IOException e2) { } catch (IOException e2) {
System.err.println("Cannot close socket"); System.err.println("Cannot close socket");
} finally {
throw new SocketClosed(); throw new SocketClosed();
} }
} }
@ -138,25 +139,27 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
case LOAD_RESPONSE: case LOAD_RESPONSE:
case LIST_RESPONSE: case LIST_RESPONSE:
case HASH_RESPONSE: case HASH_RESPONSE:
case DISCOVER_RESPONSE:
case NOT_A_TRACKER:
// we were expecting a request, but we are receiving a response // we were expecting a request, but we are receiving a response
throw new ProtocolError(); throw new ProtocolError();
default : default :
break; break;
} }
} catch (TransmissionError e) { } catch (TransmissionError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss); (new ProtocolP2PPacketTCP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss);
throw e; throw e;
} catch (ProtocolError e) { } catch (ProtocolError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(ss); (new ProtocolP2PPacketTCP<Payload>(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(ss);
throw e; throw e;
} catch (VersionError e) { } catch (VersionError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.VERSION_ERROR))).send(ss); (new ProtocolP2PPacketTCP<Payload>(new Payload(RequestResponseCode.VERSION_ERROR))).send(ss);
throw e; throw e;
} catch (InternalError e) { } catch (InternalError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss); (new ProtocolP2PPacketTCP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss);
throw e; throw e;
} catch (SizeError e) { } catch (SizeError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss); (new ProtocolP2PPacketTCP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss);
throw e; throw e;
} }
if (protocolError) { if (protocolError) {
@ -170,10 +173,10 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
* @throws IOException * @throws IOException
* @throws SocketClosed * @throws SocketClosed
*/ */
public void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException, SocketClosed { public <U extends ProtocolP2PPacket<?>> void sendResponse(U response) throws InternalError, IOException, SocketClosed {
assert response instanceof ProtocolP2PPacketTCP: "Wrong Packet type"; assert response instanceof ProtocolP2PPacketTCP: "Wrong Packet type";
if (response instanceof ProtocolP2PPacketTCP) { if (response instanceof ProtocolP2PPacketTCP) {
ProtocolP2PPacketTCP r = (ProtocolP2PPacketTCP) response; ProtocolP2PPacketTCP<?> r = (ProtocolP2PPacketTCP<?>) response;
assert responseSocket != null : "Cannot send response to a packet not received"; assert responseSocket != null : "Cannot send response to a packet not received";
if (responseSocket == null) { if (responseSocket == null) {
throw new InternalError(); throw new InternalError();
@ -188,6 +191,7 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
* @return ProtocolP2PPacket received * @return ProtocolP2PPacket received
* @throws EmptyFile * @throws EmptyFile
* @throws NotFound * @throws NotFound
* @throws NotATracker
* @throws EmptyDirectory * @throws EmptyDirectory
* @throws InternalRemoteError * @throws InternalRemoteError
* @throws VersionRemoteError * @throws VersionRemoteError
@ -200,17 +204,30 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
* @throws IOException * @throws IOException
* @throws SocketClosed * @throws SocketClosed
*/ */
public ProtocolP2PPacket receiveResponse() throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, SocketClosed { public ProtocolP2PPacket<?> receiveResponse() throws EmptyFile, NotFound, NotATracker, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, SocketClosed {
assert requestSocket != null : "Cannot receive response because request packet not sent."; assert requestSocket != null : "Cannot receive response because request packet not sent.";
if (requestSocket == null) { if (requestSocket == null) {
throw new InternalError(); throw new InternalError();
} }
// reception // reception
byte[] packet = new byte[8192]; byte[] packet = new byte[8192];
requestSocket.getInputStream().read(packet); try {
if (-1== requestSocket.getInputStream().read(packet)) {
throw new IOException();
}
} catch (IOException e) {
// Error: cannot read request, closing socket
try {
requestSocket.close();
throw new SocketClosed();
} catch (IOException e2) {
System.err.println("Cannot close socket");
throw new SocketClosed();
}
}
// contruction // contruction
try { try {
ProtocolP2PPacketTCP p = new ProtocolP2PPacketTCP(packet); ProtocolP2PPacketTCP<?> p = new ProtocolP2PPacketTCP<>(packet);
Payload payload = p.getPayload(); Payload payload = p.getPayload();
switch (payload.getRequestResponseCode()) { switch (payload.getRequestResponseCode()) {
case PROTOCOL_ERROR : case PROTOCOL_ERROR :
@ -225,23 +242,25 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
throw new NotFound(); throw new NotFound();
case EMPTY_FILE: case EMPTY_FILE:
throw new EmptyFile(); throw new EmptyFile();
case NOT_A_TRACKER:
throw new NotATracker();
default : default :
return (ProtocolP2PPacket)p; return (ProtocolP2PPacket)p;
} }
} catch (TransmissionError e) { } catch (TransmissionError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket); (new ProtocolP2PPacketTCP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e; throw e;
} catch (ProtocolError e) { } catch (ProtocolError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(requestSocket); (new ProtocolP2PPacketTCP<Payload>(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(requestSocket);
throw e; throw e;
} catch (VersionError e) { } catch (VersionError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.VERSION_ERROR))).send(requestSocket); (new ProtocolP2PPacketTCP<Payload>(new Payload(RequestResponseCode.VERSION_ERROR))).send(requestSocket);
throw e; throw e;
} catch (InternalError e) { } catch (InternalError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket); (new ProtocolP2PPacketTCP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e; throw e;
} catch (SizeError e) { } catch (SizeError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket); (new ProtocolP2PPacketTCP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e; throw e;
} }
} }
@ -288,6 +307,18 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
case HASH_RESPONSE: case HASH_RESPONSE:
payload = (Payload) new HashResponse(packet); payload = (Payload) new HashResponse(packet);
break; break;
case REGISTER:
payload = (Payload) new Register(packet);
break;
case UNREGISTER:
payload = (Payload) new Unregister(packet);
break;
case DISCOVER_REQUEST:
payload = (Payload) new DiscoverRequest(packet);
break;
case DISCOVER_RESPONSE:
payload = (Payload) new DiscoverResponse(packet);
break;
default: default:
payload = new Payload(packet); // this can throw TransmissionError payload = new Payload(packet); // this can throw TransmissionError
break; break;
@ -319,4 +350,15 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
return packet; return packet;
} }
/** Get hostItem of the sender
* @return hostItem of the sender
* @throws InternalError
*/
public HostItem getHostItem() throws InternalError {
if (responseSocket == null) {
throw new InternalError();
}
return new HostItem(responseSocket.getInetAddress().getHostName(), responseSocket.getPort());
}
} }

@ -1,10 +1,11 @@
package protocolP2P; package protocolP2P;
import exception.InternalError; import localException.InternalError;
import exception.ProtocolError; import localException.ProtocolError;
import exception.SizeError; import localException.SizeError;
import exception.TransmissionError; import localException.TransmissionError;
import exception.VersionError; import localException.VersionError;
import exception.SocketClosed; import localException.SocketClosed;
import remoteException.NotATracker;
import remoteException.EmptyDirectory; import remoteException.EmptyDirectory;
import remoteException.InternalRemoteError; import remoteException.InternalRemoteError;
import remoteException.NotFound; import remoteException.NotFound;
@ -12,13 +13,12 @@ import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError; import remoteException.VersionRemoteError;
import remoteException.EmptyFile; import remoteException.EmptyFile;
import tools.BytesArrayTools; import tools.BytesArrayTools;
import tools.HostItem;
import protocolP2P.Payload; import protocolP2P.Payload;
import protocolP2P.RequestResponseCode; import protocolP2P.RequestResponseCode;
import protocolP2P.LoadRequest; import protocolP2P.LoadRequest;
import protocolP2P.FileList; import protocolP2P.FileList;
import protocolP2P.FilePart; import protocolP2P.FilePart;
import java.util.ArrayList;
import java.lang.Byte;
import java.net.DatagramPacket; import java.net.DatagramPacket;
import java.net.DatagramSocket; import java.net.DatagramSocket;
import java.net.SocketAddress; import java.net.SocketAddress;
@ -30,17 +30,18 @@ import java.io.IOException;
* @author JS Auge * @author JS Auge
* @version 1.0 * @version 1.0
*/ */
public class ProtocolP2PPacketUDP extends ProtocolP2PPacket { public class ProtocolP2PPacketUDP < T extends Payload> extends ProtocolP2PPacket < T > {
private final static int CHECKSUM_POSITION = 2; private final static int CHECKSUM_POSITION = 2;
private HostItem remoteHost;
private SocketAddress responseSocketAddress; // socket address used when receptionning request and to sending response private SocketAddress responseSocketAddress; // socket address used when receptionning request and to sending response
private DatagramSocket responseSocket; // socket used to recept request and send response private DatagramSocket responseSocket; // socket used to recept request and send response
private DatagramSocket requestSocket; // socket used to send request and to reception response private DatagramSocket requestSocket; // socket used to send request and to reception response
/** Constructor with payload parameter (typically used when sending packet). /** Constructor with payload parameter (typically used when sending packet).
* @param payload the payload associated with the packet to send * @param payload the payload associated with the packet to send
*/ */
public ProtocolP2PPacketUDP(Payload payload) { public ProtocolP2PPacketUDP(T payload) {
super(payload); super(payload);
} }
@ -112,6 +113,7 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
DatagramPacket reception = new DatagramPacket(packet, packet.length); DatagramPacket reception = new DatagramPacket(packet, packet.length);
ss.receive(reception); ss.receive(reception);
responseSocketAddress = reception.getSocketAddress(); responseSocketAddress = reception.getSocketAddress();
remoteHost = new HostItem(reception.getAddress().getHostName(), reception.getPort());
// contruction // contruction
boolean protocolError = false; boolean protocolError = false;
try { try {
@ -130,25 +132,27 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
case LOAD_RESPONSE: case LOAD_RESPONSE:
case LIST_RESPONSE: case LIST_RESPONSE:
case HASH_RESPONSE: case HASH_RESPONSE:
case DISCOVER_RESPONSE:
case NOT_A_TRACKER:
// we were expecting a request, but we are receiving a response // we were expecting a request, but we are receiving a response
throw new ProtocolError(); throw new ProtocolError();
default : default :
break; break;
} }
} catch (TransmissionError e) { } catch (TransmissionError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss, responseSocketAddress); (new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss, responseSocketAddress);
throw e; throw e;
} catch (ProtocolError e) { } catch (ProtocolError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(ss, responseSocketAddress); (new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(ss, responseSocketAddress);
throw e; throw e;
} catch (VersionError e) { } catch (VersionError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.VERSION_ERROR))).send(ss, responseSocketAddress); (new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.VERSION_ERROR))).send(ss, responseSocketAddress);
throw e; throw e;
} catch (InternalError e) { } catch (InternalError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss, responseSocketAddress); (new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss, responseSocketAddress);
throw e; throw e;
} catch (SizeError e) { } catch (SizeError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss, responseSocketAddress); (new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss, responseSocketAddress);
throw e; throw e;
} }
if (protocolError) { if (protocolError) {
@ -161,10 +165,10 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
* @throws InternalError * @throws InternalError
* @throws IOException * @throws IOException
*/ */
public void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException { public <U extends ProtocolP2PPacket<?>>void sendResponse(U response) throws InternalError, IOException {
assert response instanceof ProtocolP2PPacketUDP: "Wrong Packet type"; assert response instanceof ProtocolP2PPacketUDP: "Wrong Packet type";
if (response instanceof ProtocolP2PPacketUDP) { if (response instanceof ProtocolP2PPacketUDP) {
ProtocolP2PPacketUDP r = (ProtocolP2PPacketUDP) response; ProtocolP2PPacketUDP<?> r = (ProtocolP2PPacketUDP<?>) response;
assert responseSocket != null : "Cannot send response to a packet not received"; assert responseSocket != null : "Cannot send response to a packet not received";
if (responseSocket == null) { if (responseSocket == null) {
throw new InternalError(); throw new InternalError();
@ -183,6 +187,7 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
* @return ProtocolP2PPacket received * @return ProtocolP2PPacket received
* @throws EmptyFile * @throws EmptyFile
* @throws NotFound * @throws NotFound
* @throws NotATracker
* @throws EmptyDirectory * @throws EmptyDirectory
* @throws InternalRemoteError * @throws InternalRemoteError
* @throws VersionRemoteError * @throws VersionRemoteError
@ -194,7 +199,7 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
* @throws SizeError * @throws SizeError
* @throws IOException * @throws IOException
*/ */
public ProtocolP2PPacket receiveResponse() throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException { public ProtocolP2PPacket<?> receiveResponse() throws EmptyFile, NotFound, NotATracker, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException {
assert requestSocket != null : "Cannot receive response because request packet not sent."; assert requestSocket != null : "Cannot receive response because request packet not sent.";
if (requestSocket == null) { if (requestSocket == null) {
throw new InternalError(); throw new InternalError();
@ -203,9 +208,10 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
byte[] packet = new byte[8192]; byte[] packet = new byte[8192];
DatagramPacket reception = new DatagramPacket(packet, packet.length); DatagramPacket reception = new DatagramPacket(packet, packet.length);
requestSocket.receive(reception); requestSocket.receive(reception);
// contruction // contruction
try { try {
ProtocolP2PPacketUDP p = new ProtocolP2PPacketUDP(packet); ProtocolP2PPacketUDP<?> p = new ProtocolP2PPacketUDP<>(packet);
Payload payload = p.getPayload(); Payload payload = p.getPayload();
switch (payload.getRequestResponseCode()) { switch (payload.getRequestResponseCode()) {
case PROTOCOL_ERROR : case PROTOCOL_ERROR :
@ -220,23 +226,25 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
throw new NotFound(); throw new NotFound();
case EMPTY_FILE: case EMPTY_FILE:
throw new EmptyFile(); throw new EmptyFile();
case NOT_A_TRACKER:
throw new NotATracker();
default : default :
return (ProtocolP2PPacket)p; return (ProtocolP2PPacket)p;
} }
} catch (TransmissionError e) { } catch (TransmissionError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket); (new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e; throw e;
} catch (ProtocolError e) { } catch (ProtocolError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(requestSocket); (new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(requestSocket);
throw e; throw e;
} catch (VersionError e) { } catch (VersionError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.VERSION_ERROR))).send(requestSocket); (new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.VERSION_ERROR))).send(requestSocket);
throw e; throw e;
} catch (InternalError e) { } catch (InternalError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket); (new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e; throw e;
} catch (SizeError e) { } catch (SizeError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket); (new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e; throw e;
} }
} }
@ -284,6 +292,18 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
case HASH_RESPONSE: case HASH_RESPONSE:
payload = (Payload) new HashResponse(packet); payload = (Payload) new HashResponse(packet);
break; break;
case REGISTER:
payload = (Payload) new Register(packet);
break;
case UNREGISTER:
payload = (Payload) new Unregister(packet);
break;
case DISCOVER_REQUEST:
payload = (Payload) new DiscoverRequest(packet);
break;
case DISCOVER_RESPONSE:
payload = (Payload) new DiscoverResponse(packet);
break;
default: default:
payload = new Payload(packet); // this can throw TransmissionError payload = new Payload(packet); // this can throw TransmissionError
break; break;
@ -353,7 +373,6 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
* @param packet full packet * @param packet full packet
* @throws TransmissionError * @throws TransmissionError
*/ */
private void checkCheckSum(byte [] packet) throws TransmissionError { private void checkCheckSum(byte [] packet) throws TransmissionError {
try { try {
int checksum = BytesArrayTools.readInt16Bits(packet, CHECKSUM_POSITION); int checksum = BytesArrayTools.readInt16Bits(packet, CHECKSUM_POSITION);
@ -365,4 +384,15 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
throw new TransmissionError(); throw new TransmissionError();
} }
} }
/** Get hostItem of the sender
* @return hostItem of the sender
* @throws InternalError
*/
public HostItem getHostItem() throws InternalError {
if (remoteHost == null) {
throw new InternalError();
}
return remoteHost;
}
} }

@ -0,0 +1,77 @@
package protocolP2P;
import protocolP2P.Payload;
import tools.HostItem;
import tools.BytesArrayTools;
import localException.SizeError;
import localException.InternalError;
import localException.TransmissionError;
import localException.ProtocolError;
/** Representation of payload for unregister.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class Register extends Payload {
private HostItem hostItem;
private static final int HOSTNAME_START_POSITION = PAYLOAD_START_POSITION + 2;
/** Constructor with hostItem (typically used by client)
* @param hostItem Host you want to register.
* @throws InternalError
*/
public Register(HostItem hostItem) throws InternalError {
super(RequestResponseCode.REGISTER);
this.hostItem = hostItem;
}
/** Constructor (typically used by server) with a byte[] parameter containing the Packet received.
* @param packet the full Packet received
* @throws SizeError
* @throws InternalError
* @throws ProtocolError
* @throws TransmissionError
*/
protected Register(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
super(packet);
int size = getPayloadSize(packet);
int port = BytesArrayTools.readInt16Bits(packet, PAYLOAD_START_POSITION);
String hostname = BytesArrayTools.readString(packet, HOSTNAME_START_POSITION, size - HOSTNAME_START_POSITION + PAYLOAD_START_POSITION);
hostItem = new HostItem(hostname, port);
}
/** Returns a byte[] containing Packet with padding.
* This Packet is still incomplete and should not be send directly.
* ProtocolP2PPacket will use this method to generate the complete Packet.
* @return Packet with padding
* @throws InternalError
*/
protected byte[] toPacket() throws InternalError {
String hostname = hostItem.getHostname();
// compute total size
int size = HOSTNAME_START_POSITION + hostname.length();
byte[] packet = new byte[size + 1]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size
setPayloadSize(size - PAYLOAD_START_POSITION, packet);
// write port to Packet
try {
BytesArrayTools.write16Bits(packet, PAYLOAD_START_POSITION, hostItem.getPort());
} catch (SizeError e) {
throw new InternalError();
}
// write hostname to Packet
BytesArrayTools.write(packet, hostname, HOSTNAME_START_POSITION);
return packet;
}
/** HostItem getter.
* @return hostItem
*/
public HostItem getHostItem() {
return hostItem;
}
}

@ -1,6 +1,6 @@
package protocolP2P; package protocolP2P;
import protocolP2P.CodeType; import protocolP2P.CodeType;
import exception.ProtocolError; import localException.ProtocolError;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.lang.Byte; import java.lang.Byte;
@ -15,16 +15,21 @@ public enum RequestResponseCode {
LIST_REQUEST(CodeType.REQUEST, (byte)0x00), LIST_REQUEST(CodeType.REQUEST, (byte)0x00),
LOAD_REQUEST(CodeType.REQUEST, (byte)0x01), LOAD_REQUEST(CodeType.REQUEST, (byte)0x01),
HASH_REQUEST(CodeType.REQUEST, (byte)0x02), HASH_REQUEST(CodeType.REQUEST, (byte)0x02),
DISCOVER_REQUEST(CodeType.REQUEST_TRACKER, (byte)0x03),
REGISTER(CodeType.REQUEST_TRACKER, (byte)0x04),
UNREGISTER(CodeType.REQUEST_TRACKER, (byte)0x05),
LIST_RESPONSE(CodeType.RESPONSE, (byte)0x80), LIST_RESPONSE(CodeType.RESPONSE, (byte)0x80),
LOAD_RESPONSE(CodeType.RESPONSE, (byte)0x81), LOAD_RESPONSE(CodeType.RESPONSE, (byte)0x81),
HASH_RESPONSE(CodeType.RESPONSE, (byte)0x82), HASH_RESPONSE(CodeType.RESPONSE, (byte)0x82),
DISCOVER_RESPONSE(CodeType.RESPONSE, (byte)0x83),
VERSION_ERROR(CodeType.ERROR, (byte)0xC0), VERSION_ERROR(CodeType.ERROR, (byte)0xC0),
PROTOCOL_ERROR(CodeType.ERROR, (byte)0xC1), PROTOCOL_ERROR(CodeType.ERROR, (byte)0xC1),
INTERNAL_ERROR(CodeType.ERROR, (byte)0xC2), INTERNAL_ERROR(CodeType.ERROR, (byte)0xC2),
EMPTY_DIRECTORY(CodeType.ERROR, (byte)0xC3), EMPTY_DIRECTORY(CodeType.ERROR, (byte)0xC3),
NOT_FOUND(CodeType.ERROR, (byte)0xC4), NOT_FOUND(CodeType.ERROR, (byte)0xC4),
EMPTY_FILE(CodeType.ERROR, (byte)0xC5); EMPTY_FILE(CodeType.ERROR, (byte)0xC5),
NOT_A_TRACKER(CodeType.ERROR, (byte)0xC6);
public final CodeType codeType; public final CodeType codeType;
public final byte codeValue; public final byte codeValue;
protected final static int RRCODE_POSITION = 1; protected final static int RRCODE_POSITION = 1;
@ -62,7 +67,5 @@ public enum RequestResponseCode {
return r; return r;
} }
}
}

@ -0,0 +1,77 @@
package protocolP2P;
import protocolP2P.Payload;
import tools.HostItem;
import tools.BytesArrayTools;
import localException.SizeError;
import localException.InternalError;
import localException.TransmissionError;
import localException.ProtocolError;
/** Representation of payload for unregister.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class Unregister extends Payload {
private HostItem hostItem;
private static final int HOSTNAME_START_POSITION = PAYLOAD_START_POSITION + 2;
/** Constructor with hostItem (typically used by client)
* @param hostItem Host you want to register.
* @throws InternalError
*/
public Unregister(HostItem hostItem) throws InternalError {
super(RequestResponseCode.UNREGISTER);
this.hostItem = hostItem;
}
/** Constructor (typically used by server) with a byte[] parameter containing the Packet received.
* @param packet the full Packet received
* @throws SizeError
* @throws InternalError
* @throws ProtocolError
* @throws TransmissionError
*/
protected Unregister(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
super(packet);
int size = getPayloadSize(packet);
int port = BytesArrayTools.readInt16Bits(packet, PAYLOAD_START_POSITION);
String hostname = BytesArrayTools.readString(packet, HOSTNAME_START_POSITION, size - HOSTNAME_START_POSITION + PAYLOAD_START_POSITION);
hostItem = new HostItem(hostname, port);
}
/** Returns a byte[] containing Packet with padding.
* This Packet is still incomplete and should not be send directly.
* ProtocolP2PPacket will use this method to generate the complete Packet.
* @return Packet with padding
* @throws InternalError
*/
protected byte[] toPacket() throws InternalError {
String hostname = hostItem.getHostname();
// compute total size
int size = HOSTNAME_START_POSITION + hostname.length();
byte[] packet = new byte[size + 1]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size
setPayloadSize(size - PAYLOAD_START_POSITION, packet);
// write port to Packet
try {
BytesArrayTools.write16Bits(packet, PAYLOAD_START_POSITION, hostItem.getPort());
} catch (SizeError e) {
throw new InternalError();
}
// write hostname to Packet
BytesArrayTools.write(packet, hostname, HOSTNAME_START_POSITION);
return packet;
}
/** HostItem getter.
* @return hostItem
*/
public HostItem getHostItem() {
return hostItem;
}
}

@ -1,4 +1,7 @@
package remoteException; package remoteException;
public class EmptyDirectory extends Exception {
import exception.RemoteException;
public class EmptyDirectory extends RemoteException {
private static final long serialVersionUID = 12L; private static final long serialVersionUID = 12L;
} }

@ -1,4 +1,7 @@
package remoteException; package remoteException;
public class EmptyFile extends Exception {
import exception.RemoteException;
public class EmptyFile extends RemoteException {
private static final long serialVersionUID = 12L; private static final long serialVersionUID = 12L;
} }

@ -1,4 +1,7 @@
package remoteException; package remoteException;
public class InternalRemoteError extends Exception {
import exception.RemoteException;
public class InternalRemoteError extends RemoteException {
private static final long serialVersionUID = 12L; private static final long serialVersionUID = 12L;
} }

@ -0,0 +1,7 @@
package remoteException;
import exception.RemoteException;
public class NotATracker extends RemoteException {
private static final long serialVersionUID = 12L;
}

@ -1,4 +1,7 @@
package remoteException; package remoteException;
public class NotFound extends Exception {
import exception.RemoteException;
public class NotFound extends RemoteException {
private static final long serialVersionUID = 12L; private static final long serialVersionUID = 12L;
} }

@ -1,4 +1,7 @@
package remoteException; package remoteException;
public class ProtocolRemoteError extends Exception {
import exception.RemoteException;
public class ProtocolRemoteError extends RemoteException {
private static final long serialVersionUID = 12L; private static final long serialVersionUID = 12L;
} }

@ -1,4 +1,7 @@
package remoteException; package remoteException;
public class VersionRemoteError extends Exception {
import exception.RemoteException;
public class VersionRemoteError extends RemoteException {
private static final long serialVersionUID = 12L; private static final long serialVersionUID = 12L;
} }

@ -0,0 +1,156 @@
package serverP2P;
import tools.Logger;
import tools.LogLevel;
import tools.HostItem;
import java.io.File;
import java.util.Vector;
import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import protocolP2P.HashAlgorithm;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.io.IOException;
/** Class allowing to keep the tracker informed about file list
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public abstract class FileWatcher implements Runnable {
protected String[] fileList = new String[0];
protected Logger logger;
protected volatile boolean stop;
protected long time;
protected boolean force;
protected HostItem server;
protected HostItem tracker;
protected String baseDirectory;
protected Map<String, byte[]> sha512 = new HashMap<>();
/** Constructor
* @param logger Logger
* @param millis Time interval before recheck
* @param server HostItem for the server
* @param tracker HostItem for the tracker
* @param baseDirectory Directory to search files
*/
public FileWatcher(Logger logger, long millis, HostItem server, HostItem tracker, String baseDirectory) {
assert logger != null : "Logger is null";
assert server != null : "Server is null";
assert tracker != null : "Tracker is null";
assert baseDirectory != null : "baseDirectory is null";
this.logger = logger;
time = millis;
this.server = server;
this.tracker = tracker;
this.baseDirectory = baseDirectory;
}
/** FileList getter
* @return fileList
*/
public String[] getFileList() {
return fileList;
}
/** Sha512 map getter
* @return sha512 hashmap
*/
public Map<String, byte[]> getSha512Map() {
return sha512;
}
/** Allow a manual check
*/
public void trigger() {
if (updateFileList() || force) {
force = false;
writeLog("File list watcher detected changes. Informing tracker.", LogLevel.Info);
registerTracker();
}
}
/** Runnable implementation */
public void run() {
writeLog("File list watcher started : delay " + time + " milliseconds.", LogLevel.Info);
while(!stop) {
trigger();
try {
Thread.sleep(time);
} catch(InterruptedException e) {
writeLog("File list watcher interrupted", LogLevel.Error);
setStop();
}
}
}
/** Register server on tracker
*/
protected abstract void registerTracker();
/** Update fileList and returns true if different than old list.
* @return true if changed
*/
protected boolean updateFileList() {
File folder = new File(baseDirectory);
Vector<String> v = new Vector<String>();
File[] files = folder.listFiles();
/* Add non-recursively files's names to fileList */
for (File f : files) {
if (f.isFile()) {
v.add(f.getName());
}
}
String[] newFileList = new String[v.size()];
v.toArray(newFileList);
Arrays.sort(newFileList);
if (!Arrays.equals(newFileList, fileList)) {
fileList = newFileList;
initSha512();
return true;
} else {
return false;
}
}
/** Ask the thread to stop
*/
public void setStop() {
stop = true;
}
/** Init sha512 map.
*/
protected void initSha512() {
for(String f: fileList) {
try {
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
sha512.put(f, md.digest(Files.readAllBytes(Paths.get(baseDirectory + f))));
md.reset();
} catch (NoSuchAlgorithmException e) {
writeLog("sha512 not supported", LogLevel.Error);
} catch (IOException e) {
writeLog("cannot read " + f, LogLevel.Warning);
}
}
}
/** Implementation of writeLog
* @param text Text to log
* @param logLevel level of logging
*/
protected abstract void writeLog(String text, LogLevel logLevel);
/** Implementation of writeLog
* @param e exception to log
* @param logLevel level of logging
*/
protected abstract void writeLog(Exception e, LogLevel logLevel);
}

@ -0,0 +1,65 @@
package serverP2P;
import tools.Logger;
import tools.LogLevel;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.ProtocolP2PPacketTCP;
import protocolP2P.Register;
import protocolP2P.Payload;
import tools.HostItem;
/** Class allowing to keep the tracker informed about file list (TCP impl.)
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class FileWatcherTCP extends FileWatcher {
/** Constructor
* @param logger Logger
* @param millis Time interval before recheck
* @param server HostItem for the server
* @param tracker HostItem for the tracker
* @param baseDirectory Directory to search files
*/
public FileWatcherTCP(Logger logger, long millis, HostItem server, HostItem tracker, String baseDirectory) {
super(logger, millis, server, tracker, baseDirectory);
assert logger != null : "Logger is null";
assert server != null : "Server is null";
assert tracker != null : "Tracker is null";
assert baseDirectory != null : "baseDirectory is null";
}
/** Implementation of writeLog
* @param text Text to log
* @param logLevel level of logging
*/
protected void writeLog(String text, LogLevel logLevel) {
logger.writeTCP(text, logLevel);
}
/** Implementation of writeLog
* @param e exception to log
* @param logLevel level of logging
*/
protected void writeLog(Exception e, LogLevel logLevel) {
logger.writeTCP(e, logLevel);
}
/** Register server on tracker
*/
protected void registerTracker() {
try {
writeLog("Trying to into tracker", LogLevel.Info);
ProtocolP2PPacket<Register> p = (ProtocolP2PPacket<Register>)new ProtocolP2PPacketTCP<Register>(new Register(server));
p.sendRequest((Object)tracker.tryGetTCPSocket());
writeLog("Register request sent.", LogLevel.Debug);
tracker.closeTCPSocket();
} catch (Exception e) {
// error, trying again at next iteration
force = true;
writeLog("Cannot contact tracker, trying again at next iteration (" + time + " milliseconds).", LogLevel.Error);
}
}
}

@ -0,0 +1,64 @@
package serverP2P;
import tools.Logger;
import tools.LogLevel;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.ProtocolP2PPacketUDP;
import protocolP2P.Register;
import protocolP2P.Payload;
import tools.HostItem;
/** Class allowing to keep the tracker informed about file list (UDP impl.)
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class FileWatcherUDP extends FileWatcher {
/** Constructor
* @param logger Logger
* @param millis Time interval before recheck
* @param server HostItem for the server
* @param tracker HostItem for the tracker
* @param baseDirectory Directory to search files
*/
public FileWatcherUDP(Logger logger, long millis, HostItem server, HostItem tracker, String baseDirectory) {
super(logger, millis, server, tracker, baseDirectory);
assert logger != null : "Logger is null";
assert server != null : "Server is null";
assert tracker != null : "Tracker is null";
assert baseDirectory != null : "baseDirectory is null";
}
/** Implementation of writeLog
* @param text Text to log
* @param logLevel level of logging
*/
protected void writeLog(String text, LogLevel logLevel) {
logger.writeUDP(text, logLevel);
}
/** Implementation of writeLog
* @param e exception to log
* @param logLevel level of logging
*/
protected void writeLog(Exception e, LogLevel logLevel) {
logger.writeUDP(e, logLevel);
}
/** Register server on tracker
*/
protected void registerTracker() {
try {
writeLog("Trying to register into tracker", LogLevel.Info);
ProtocolP2PPacket<Register> p = (ProtocolP2PPacket<Register>)new ProtocolP2PPacketUDP<Register>(new Register(server));
p.sendRequest((Object)tracker.getUDPSocket());
writeLog("Register request sent (but cannot ensure reception).", LogLevel.Debug);
tracker.closeUDPSocket();
} catch (Exception e) {
force = true;
writeLog("Cannot contact tracker, trying again at next iteration (" + time + " milliseconds).", LogLevel.Error);
}
}
}

@ -0,0 +1,234 @@
package serverP2P;
import serverP2P.FileWatcher;
import tools.Logger;
import tools.LogLevel;
import tools.HostItem;
import tools.ServeErrors;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.Payload;
import protocolP2P.RequestResponseCode;
import protocolP2P.FileList;
import protocolP2P.FilePart;
import protocolP2P.LoadRequest;
import protocolP2P.HashRequest;
import protocolP2P.HashResponse;
import protocolP2P.HashAlgorithm;
import protocolP2P.Unregister;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;
import java.io.IOException;
import exception.LocalException;
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public abstract class ServerManagement extends ServeErrors implements Runnable {
protected volatile boolean stop;
protected FileWatcher fileListWatcher;
protected Logger logger;
protected String baseDirectory;
protected HostItem server;
protected HostItem tracker;
/** Constructor */
public ServerManagement(String baseDirectory, HostItem server, HostItem tracker, Logger logger) {
super();
assert baseDirectory != null : "baseDirectory is null";
assert server != null : "server is null";
assert tracker != null : "tracker is null";
assert logger != null : "logger is null";
stop = false;
this.baseDirectory = baseDirectory;
this.server = server;
this.tracker = tracker;
this.logger = logger;
}
/** Stop the thread */
public void setStop() {
stop = true;
}
/** Trigger a manual check of the file list
*/
public void updateFileList() {
if (fileListWatcher != null) {
fileListWatcher.trigger();
}
}
/** Send response to list request
* @param pd Request received
*/
protected < T extends ProtocolP2PPacket<?> > void sendListResponse(T pd) {
try {
String[] fileList = fileListWatcher.getFileList();
if (fileList.length == 0) {
writeLog("Sending EMPTY_DIRECTORY to host " + pd.getHostItem(), LogLevel.Action);
sendEmptyDirectory(pd);
} else {
writeLog("Sending LIST_RESPONSE to host " + pd.getHostItem(), LogLevel.Action);
pd.sendResponse(createProtocolP2PPacket((Payload)(new FileList(fileList))));
}
} catch (Exception e2) {
writeLog(e2, LogLevel.Error);
}
}
/** Send hash response to hash request
* @param pd Request received
*/
protected < T extends ProtocolP2PPacket<?> > void sendHashResponse(T pd) {
Payload p = pd.getPayload();
assert p instanceof HashRequest : "payload must be an instance of HashRequest";
if (!(p instanceof HashRequest)) {
sendInternalError(pd);
} else {
String filename = ((HashRequest)p).getFilename();
if (Arrays.binarySearch(fileListWatcher.getFileList(), filename) >= 0) {
Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
for (HashAlgorithm h : ((HashRequest)p).getAlgoList()) {
switch (h) {
case SHA512:
hashes.put(h, fileListWatcher.getSha512Map().get(filename));
break;
case MD5:
default:
hashes.put(h, new byte[0]);
break;
}
}
try {
pd.sendResponse(createProtocolP2PPacket((Payload)(new HashResponse(filename, hashes))));
} catch (Exception e) {
writeLog(e, LogLevel.Error);
}
} else {
// file not found
try {
sendNotFound(pd);
} catch (Exception e) {
writeLog(e, LogLevel.Error);
}
}
}
}
/** Send response to load request
* @param pd Request received
*/
protected < T extends ProtocolP2PPacket<?> > void sendLoadResponse(T pd) {
Payload p = pd.getPayload();
assert p instanceof LoadRequest : "payload must be an instance of LoadRequest";
if (!(p instanceof LoadRequest)) {
sendInternalError(pd);
} else {
String filename = ((LoadRequest)p).getFilename();
long offset = ((LoadRequest)p).getOffset();
long maxSizePartialContent = ((LoadRequest)p).getMaxSizePartialContent();
try {
byte[] fullLoad = Files.readAllBytes(Paths.get(baseDirectory + filename));
long sizeToSend = 0;
if (fullLoad.length - offset < maxSizePartialContent) {
writeLog("Sending last partialContent", LogLevel.Debug);
sizeToSend = fullLoad.length - offset;
} else {
sizeToSend = maxSizePartialContent;
}
writeLog("maxSizePartialContent: " + maxSizePartialContent, LogLevel.Debug);
writeLog("Sending " + filename + " from " + offset + " to " + (offset + sizeToSend), LogLevel.Debug);
byte[] load = Arrays.copyOfRange(fullLoad, (int)offset, (int)(offset + sizeToSend));
String[] fileList = fileListWatcher.getFileList();
if (Arrays.binarySearch(fileList, filename) >= 0) {
try {
if (load.length == 0) {
sendEmptyFile(pd);
} else {
pd.sendResponse(createProtocolP2PPacket((Payload)(new FilePart(filename, fullLoad.length, offset, load))));
}
} catch (Exception e2) {
writeLog(e2, LogLevel.Error);
}
} else {
writeLog("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename), LogLevel.Debug);
writeLog("File list:", LogLevel.Debug);
for (String f: fileList) {
writeLog("- " + f, LogLevel.Debug);
}
throw new IOException(); // to send a NOT_FOUND in the catch block
}
} catch (IOException e) {
try {
sendNotFound(pd);
} catch (Exception e2) {
writeLog(e2, LogLevel.Debug);
}
}
}
}
/** Getter for tracker socket
*/
protected abstract Object getTrackerSocket();
/** Send unregister request to tracker
*/
protected void sendUnregisterRequest() {
// unregistering from tracker
try {
writeLog("Unregistering from tracker", LogLevel.Info);
createProtocolP2PPacket(new Unregister(server)).sendRequest(getTrackerSocket());
} catch (Exception e) {
writeLog("Cannot unregister from tracker", LogLevel.Error);
writeLog(e, LogLevel.Error);
}
}
/** Handle request.
* @throws LocalException
*/
protected < T extends ProtocolP2PPacket<?> > void handleRequest(T pd) throws LocalException {
Payload p = pd.getPayload();
switch (p.getRequestResponseCode()) {
case LOAD_REQUEST:
writeLog("Received LOAD_REQUEST from host " + pd.getHostItem(), LogLevel.Action);
sendLoadResponse(pd);
break;
case LIST_REQUEST:
writeLog("Received LIST_REQUEST from host " + pd.getHostItem(), LogLevel.Action);
sendListResponse(pd);
break;
case HASH_REQUEST:
writeLog("Received HASH_REQUEST from host " + pd.getHostItem(), LogLevel.Action);
sendHashResponse(pd);
break;
case DISCOVER_REQUEST:
writeLog("Received DISCOVER_REQUEST from host " + pd.getHostItem(), LogLevel.Action);
sendNotATracker(pd);
break;
case UNREGISTER:
writeLog("Received UNREGISTER from host " + pd.getHostItem(), LogLevel.Action);
sendNotATracker(pd);
break;
case REGISTER:
writeLog("Received REGISTER from host " + pd.getHostItem(), LogLevel.Action);
sendNotATracker(pd);
break;
default:
writeLog("Received grabbage from host " + pd.getHostItem(), LogLevel.Action);
sendInternalError(pd);
}
}
}

@ -17,26 +17,26 @@ import protocolP2P.Payload;
import protocolP2P.LoadRequest; import protocolP2P.LoadRequest;
import protocolP2P.FileList; import protocolP2P.FileList;
import protocolP2P.FilePart; import protocolP2P.FilePart;
import exception.InternalError; import localException.InternalError;
import exception.ProtocolError; import localException.ProtocolError;
import exception.SizeError; import localException.SizeError;
import exception.TransmissionError; import localException.TransmissionError;
import exception.VersionError; import localException.VersionError;
import exception.SocketClosed; import localException.SocketClosed;
import remoteException.EmptyDirectory; import exception.LocalException;
import remoteException.InternalRemoteError;
import remoteException.NotFound;
import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.EmptyFile;
import java.util.Arrays; import java.util.Arrays;
import tools.Logger; import tools.Logger;
import tools.LogLevel; import tools.LogLevel;
import tools.HostItem;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import protocolP2P.HashAlgorithm; import protocolP2P.HashAlgorithm;
import protocolP2P.HashRequest; import protocolP2P.HashRequest;
import protocolP2P.HashResponse; import protocolP2P.HashResponse;
import protocolP2P.Register;
import protocolP2P.Unregister;
import serverP2P.ServerManagement;
import serverP2P.FileWatcherTCP;
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for TCP. /** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for TCP.
@ -45,268 +45,121 @@ import protocolP2P.HashResponse;
* @author JS Auge * @author JS Auge
* @version 1.0 * @version 1.0
*/ */
public class ServerManagementTCP implements Runnable { public class ServerManagementTCP extends ServerManagement {
private String[] fileList;
private Map<String, byte[]> sha512 = new HashMap<>();
private String baseDirectory;
private int TCPPort;
private ServerSocket socket; private ServerSocket socket;
private Logger logger;
/** Constructor for TCP implementation, with baseDirectory and TCPPort parameters. /** Constructor for TCP implementation, with baseDirectory and TCPPort parameters.
* @param baseDirectory the root directory where files are stored * @param baseDirectory the root directory where files are stored
* @param hostName the server will bind on this address
* @param TCPPort the server will listen on this port * @param TCPPort the server will listen on this port
* @param logger Logger item
* @param tracker Tracker
*/ */
public ServerManagementTCP(String baseDirectory, int TCPPort, Logger logger) { public ServerManagementTCP(String baseDirectory, HostItem server, HostItem tracker, Logger logger) {
this.logger = logger; super(baseDirectory, server, tracker, logger);
this.baseDirectory = baseDirectory; assert baseDirectory != null : "baseDirectory is null";
this.TCPPort = TCPPort; assert server != null : "server is null";
initFileList(); assert tracker != null : "tracker is null";
initSha512(); assert logger != null : "logger is null";
try { try {
socket = new ServerSocket(TCPPort); socket = new ServerSocket(server.getPort(), 10, server.getInetAddress());
} catch (SocketException e) { } catch (SocketException e) {
logger.writeTCP("Error: cannot listen on port " + TCPPort, LogLevel.Error); writeLog("Error: cannot listen on " + server, LogLevel.Error);
System.exit(-1); System.exit(-1);
} catch (IOException e) { } catch (IOException e) {
logger.writeTCP("Error: cannot openning socket", LogLevel.Error); writeLog("Error: cannot openning socket", LogLevel.Error);
System.exit(-2); System.exit(-2);
} }
} }
/** Implementation of runnable. This methods allows to run the server. /** Implementation of runnable. This methods allows to run the server.
*/ */
public void run() { public void run() {
logger.writeTCP("Server sucessfully started", LogLevel.Info); writeLog("Server sucessfully started", LogLevel.Info);
do { fileListWatcher = (FileWatcher)new FileWatcherTCP(logger, 10000, server, tracker, baseDirectory); // checking every 10 seconds
(new Thread(fileListWatcher)).start();
while(!stop) {
try { try {
Socket s = socket.accept(); Socket s = socket.accept();
ClientHandler c = new ClientHandler(s); ClientHandler c = new ClientHandler(s);
(new Thread(c)).start(); (new Thread(c)).start();
} catch (IOException e) { } catch (IOException e) {
logger.writeTCP("Error while accepting new connection", LogLevel.Warning); writeLog("Error while accepting new connection", LogLevel.Warning);
} }
} while(true); }
fileListWatcher.setStop();
sendUnregisterRequest();
} }
/** Private runnable class allowing to serve one client. /** Private runnable class allowing to serve one client.
*/ */
private class ClientHandler implements Runnable { private class ClientHandler implements Runnable {
private Socket s; private HostItem addr;
private String addr;
/** Constructor with a socket. /** Constructor with a socket.
* @param s Socket of this client * @param s Socket of this client
*/ */
public ClientHandler(Socket s) { public ClientHandler(Socket s) {
this.s = s; addr = new HostItem(s);
this.addr = "[" +s.getInetAddress().getHostAddress() + "]:" + s.getPort() + " ";
} }
/** Implementation of runnable. This method allow to serve one client. /** Implementation of runnable. This method allow to serve one client.
*/ */
public void run() { public void run() {
boolean end = false; boolean end = false;
logger.writeTCP(addr + "New connection", LogLevel.Action); writeLog("[" + addr + "] New connection", LogLevel.Action);
do { do {
end = handleRequest(); end = handleClientRequest();
} while(!end); } while(!end);
logger.writeTCP(addr + "End of connection", LogLevel.Action); writeLog("[" + addr + "] End of connection", LogLevel.Action);
} }
/** Respond to next request incomming on socket s. /** Respond to next request incomming on socket s.
* @param s Socket used to read request and send response * @param s Socket used to read request and send response
* @return true if cannot expect another request (ie, socket is closed) * @return true if cannot expect another request (ie, socket is closed)
*/ */
private boolean handleRequest() { private boolean handleClientRequest() {
try { try {
ProtocolP2PPacketTCP pd = new ProtocolP2PPacketTCP((Object)s); ProtocolP2PPacketTCP<?> pd = new ProtocolP2PPacketTCP<>((Object)addr.getTCPSocket());
Payload p = pd.getPayload(); handleRequest(pd);
switch (p.getRequestResponseCode()) {
case LOAD_REQUEST:
logger.writeTCP(addr + "LOAD_REQUEST", LogLevel.Action);
sendLoadResponse(pd);
break;
case LIST_REQUEST:
logger.writeTCP(addr + "LIST_REQUEST", LogLevel.Action);
sendListResponse(pd);
break;
case HASH_REQUEST:
logger.writeTCP(addr + "HASH_REQUEST", LogLevel.Action);
sendHashResponse(pd);
break;
default:
logger.writeTCP(addr + "Received grabbage", LogLevel.Action);
sendInternalError(pd);
}
} catch (IOException e) { } catch (IOException e) {
return true; return true;
} catch (SocketClosed e) { } catch (SocketClosed e) {
return true; return true;
} }
catch (TransmissionError e) {} catch (LocalException e) {}
catch (ProtocolError e) {}
catch (VersionError e) {}
catch (InternalError e) {}
catch (SizeError e) {}
return false; return false;
} }
} }
/** Initialize local list of all files allowed to be shared. /** Implementation of writeLog
* @param text Text to log
* @param logLevel level of logging
*/ */
private void initFileList() { protected void writeLog(String text, LogLevel logLevel) {
File folder = new File(baseDirectory); logger.writeTCP(text, logLevel);
Vector<String> v = new Vector<String>();
File[] files = folder.listFiles();
/* Add non-recursively files's names to fileList */
for (File f : files) {
if (f.isFile()) {
v.add(f.getName());
}
}
fileList = new String[v.size()];
v.toArray(fileList);
Arrays.sort(fileList);
} }
/** Init sha512 map. /** Implementation of writeLog
* @param e exception to log
* @param logLevel level of logging
*/ */
private void initSha512() { protected void writeLog(Exception e, LogLevel logLevel) {
for(String f: fileList) { logger.writeTCP(e, logLevel);
try {
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
sha512.put(f, md.digest(Files.readAllBytes(Paths.get(baseDirectory + f))));
md.reset();
} catch (NoSuchAlgorithmException e) {
logger.writeTCP("sha512 not supported", LogLevel.Error);
} catch (IOException e) {
logger.writeTCP("cannot read " + f, LogLevel.Warning);
}
}
} }
/** Send an internal error message. /** Create packets
* @param pd ProtocolP2PPacketTCP to respond * @param payload Payload
*/ */
private void sendInternalError(ProtocolP2PPacketTCP pd) { protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
logger.writeTCP("Internal Error", LogLevel.Warning); return (ProtocolP2PPacket<T>)new ProtocolP2PPacketTCP<T>(payload);
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR)));
} catch (Exception e) {
logger.writeTCP(e, LogLevel.Error);
}
} }
/** Send response to list request /** Getter for tracker socket
* @param pd Request received
*/ */
private void sendListResponse(ProtocolP2PPacketTCP pd) { protected Object getTrackerSocket() {
try { return (Object)tracker.getTCPSocket();
if (fileList.length == 0) {
logger.writeTCP("Sending EMPTY_DIRECTORY", LogLevel.Action);
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.EMPTY_DIRECTORY)));
} else {
logger.writeTCP("Sending LIST_RESPONSE", LogLevel.Action);
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new FileList(fileList))));
}
} catch (Exception e2) {
logger.writeTCP(e2, LogLevel.Error);
}
} }
/** Send hash response to hash request
* @param pd Request received
*/
private void sendHashResponse(ProtocolP2PPacketTCP pd) {
Payload p = pd.getPayload();
assert p instanceof HashRequest : "payload must be an instance of HashRequest";
if (!(p instanceof HashRequest)) {
sendInternalError(pd);
} else {
String filename = ((HashRequest)p).getFilename();
if (Arrays.binarySearch(fileList, filename) >= 0) {
Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
for (HashAlgorithm h : ((HashRequest)p).getAlgoList()) {
switch (h) {
case SHA512:
hashes.put(h, sha512.get(filename));
break;
case MD5:
default:
hashes.put(h, new byte[0]);
break;
}
}
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new HashResponse(filename, hashes))));
} catch (Exception e) {
logger.writeTCP(e, LogLevel.Error);
}
} else {
// file not found
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_FOUND)));
} catch (Exception e) {
logger.writeTCP(e, LogLevel.Error);
}
}
}
}
/** Send response to load request
* @param pd Request received
*/
private void sendLoadResponse(ProtocolP2PPacketTCP pd) {
Payload p = pd.getPayload();
assert p instanceof LoadRequest : "payload must be an instance of LoadRequest";
if (!(p instanceof LoadRequest)) {
sendInternalError(pd);
} else {
String filename = ((LoadRequest)p).getFilename();
long offset = ((LoadRequest)p).getOffset();
long maxSizePartialContent = ((LoadRequest)p).getMaxSizePartialContent();
try {
byte[] fullLoad = Files.readAllBytes(Paths.get(baseDirectory + filename));
long sizeToSend = 0;
if (fullLoad.length - offset < maxSizePartialContent) {
logger.writeTCP("Sending last partialContent", LogLevel.Debug);
sizeToSend = fullLoad.length - offset;
} else {
sizeToSend = maxSizePartialContent;
}
logger.writeTCP("maxSizePartialContent: " + maxSizePartialContent, LogLevel.Debug);
logger.writeTCP("Sending " + filename + " from " + offset + " to " + (offset + sizeToSend), LogLevel.Debug);
byte[] load = Arrays.copyOfRange(fullLoad, (int)offset, (int)(offset + sizeToSend));
if (Arrays.binarySearch(fileList, filename) >= 0) {
try {
if (load.length == 0) {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.EMPTY_FILE)));
} else {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new FilePart(filename, fullLoad.length, offset, load))));
}
} catch (Exception e2) {
logger.writeTCP(e2, LogLevel.Error);
}
} else {
logger.writeTCP("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename), LogLevel.Debug);
logger.writeTCP("File list:", LogLevel.Debug);
for (String f: fileList) {
logger.writeTCP("- " + f, LogLevel.Debug);
}
throw new IOException(); // to send a NOT_FOUND in the catch block
}
} catch (IOException e) {
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_FOUND)));
} catch (Exception e2) {
logger.writeTCP(e2, LogLevel.Debug);
}
}
}
}
} }

@ -16,18 +16,13 @@ import protocolP2P.Payload;
import protocolP2P.LoadRequest; import protocolP2P.LoadRequest;
import protocolP2P.FileList; import protocolP2P.FileList;
import protocolP2P.FilePart; import protocolP2P.FilePart;
import exception.InternalError; import localException.InternalError;
import exception.ProtocolError; import localException.ProtocolError;
import exception.SizeError; import localException.SizeError;
import exception.TransmissionError; import localException.TransmissionError;
import exception.VersionError; import localException.VersionError;
import exception.SocketClosed; import localException.SocketClosed;
import remoteException.EmptyDirectory; import exception.LocalException;
import remoteException.InternalRemoteError;
import remoteException.NotFound;
import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.EmptyFile;
import java.util.Arrays; import java.util.Arrays;
import tools.Logger; import tools.Logger;
import tools.LogLevel; import tools.LogLevel;
@ -36,6 +31,11 @@ import java.util.Map;
import protocolP2P.HashAlgorithm; import protocolP2P.HashAlgorithm;
import protocolP2P.HashRequest; import protocolP2P.HashRequest;
import protocolP2P.HashResponse; import protocolP2P.HashResponse;
import tools.HostItem;
import protocolP2P.Register;
import protocolP2P.Unregister;
import java.net.UnknownHostException;
import serverP2P.ServerManagement;
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for UDP. /** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for UDP.
* @author Louis Royer * @author Louis Royer
@ -43,206 +43,77 @@ import protocolP2P.HashResponse;
* @author JS Auge * @author JS Auge
* @version 1.0 * @version 1.0
*/ */
public class ServerManagementUDP implements Runnable { public class ServerManagementUDP extends ServerManagement {
private String[] fileList;
private Map<String, byte[]> sha512 = new HashMap<>();
private String baseDirectory;
private int UDPPort;
private DatagramSocket socket; private DatagramSocket socket;
private Logger logger;
/** Constructor for UDP implementation, with baseDirectory and UDPPort parameters. /** Constructor for UDP implementation, with baseDirectory and UDPPort parameters.
* @param baseDirectory the root directory where files are stored * @param baseDirectory the root directory where files are stored
* @param UDPPort the server will listen on this port * @param hostName the server will bind on this address
* @param port the server will listen on this port
* @param logger Logger item
* @param tracker Tracker
*/ */
public ServerManagementUDP(String baseDirectory, int UDPPort, Logger logger) { public ServerManagementUDP(String baseDirectory, HostItem server, HostItem tracker, Logger logger) {
this.logger = logger; super(baseDirectory, server, tracker, logger);
this.baseDirectory = baseDirectory; assert baseDirectory != null : "baseDirectory is null";
this.UDPPort = UDPPort; assert server != null : "server is null";
initFileList(); assert tracker != null : "tracker is null";
initSha512(); assert logger != null : "logger is null";
try { try {
socket = new DatagramSocket(UDPPort); socket = new DatagramSocket(server.getPort(), server.getInetAddress());
} catch (SocketException e) { } catch (SocketException e) {
logger.writeUDP("Error: cannot listen on port " + UDPPort, LogLevel.Error); logger.writeUDP("Error: cannot listen on " + server, LogLevel.Error);
System.exit(-1); System.exit(-1);
} }
} }
/** Implementation of runnable. This methods allows to run the server. /** Implementation of runnable. This methods allows to run the server.
*/ */
public void run() { public void run() {
logger.writeUDP("Server sucessfully started", LogLevel.Info); logger.writeUDP("Server sucessfully started", LogLevel.Info);
while(true) { fileListWatcher = (FileWatcher)new FileWatcherUDP(logger, 10000, server, tracker, baseDirectory); // checking every 10 seconds
(new Thread(fileListWatcher)).start();
while(!stop) {
try { try {
ProtocolP2PPacketUDP pd = new ProtocolP2PPacketUDP((Object)socket); ProtocolP2PPacketUDP<?> pd = new ProtocolP2PPacketUDP<>((Object)socket);
Payload p = pd.getPayload(); handleRequest(pd);
switch (p.getRequestResponseCode()) {
case LOAD_REQUEST:
logger.writeUDP("Received LOAD_REQUEST", LogLevel.Action);
assert p instanceof LoadRequest : "payload must be an instance of LoadRequest";
if (!(p instanceof LoadRequest)) {
sendInternalError(pd);
} else {
String filename = ((LoadRequest)p).getFilename();
long offset = ((LoadRequest)p).getOffset();
long maxSizePartialContent = ((LoadRequest)p).getMaxSizePartialContent();
try {
byte[] fullLoad = Files.readAllBytes(Paths.get(baseDirectory + filename));
long sizeToSend = 0;
if (fullLoad.length - offset < maxSizePartialContent) {
logger.writeUDP("Sending last partialContent", LogLevel.Debug);
sizeToSend = fullLoad.length - offset;
} else {
sizeToSend = maxSizePartialContent;
}
logger.writeUDP("maxSizePartialContent: " + maxSizePartialContent, LogLevel.Debug);
logger.writeUDP("Sending " + filename + " from " + offset + " to " + (offset + sizeToSend), LogLevel.Debug);
byte[] load = Arrays.copyOfRange(fullLoad, (int)offset, (int)(offset + sizeToSend));
if (Arrays.binarySearch(fileList, filename) >= 0) {
try {
if (load.length == 0) {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.EMPTY_FILE)));
} else {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)(new FilePart(filename, fullLoad.length, offset, load))));
}
} catch (Exception e2) {
logger.writeUDP(e2, LogLevel.Error);
}
} else {
logger.writeUDP("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename), LogLevel.Debug);
logger.writeUDP("File list:", LogLevel.Debug);
for (String f: fileList) {
logger.writeUDP("- " + f, LogLevel.Debug);
}
throw new IOException(); // to send a NOT_FOUND in the catch block
}
} catch (IOException e) {
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.NOT_FOUND)));
} catch (Exception e2) {
logger.writeUDP(e2, LogLevel.Error);
}
}
}
break;
case LIST_REQUEST:
logger.writeUDP("Received LIST_REQUEST", LogLevel.Action);
try {
if (fileList.length == 0) {
logger.writeUDP("Sending EMPTY_DIRECTORY", LogLevel.Action);
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.EMPTY_DIRECTORY)));
} else {
logger.writeUDP("Sending LIST_RESPONSE", LogLevel.Action);
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)(new FileList(fileList))));
}
} catch (Exception e2) {
logger.writeUDP(e2, LogLevel.Error);
}
break;
case HASH_REQUEST:
logger.writeUDP("Received HASH_REQUEST", LogLevel.Action);
sendHashResponse(pd);
break;
default:
sendInternalError(pd);
}
} catch (IOException e) { } catch (IOException e) {
} catch (SocketClosed e) { } catch (LocalException e) {
} catch (TransmissionError e) {
} catch (ProtocolError e) {
} catch (VersionError e) {
} catch (InternalError e) {
} catch (SizeError e) {
} }
} }
fileListWatcher.setStop();
sendUnregisterRequest();
} }
/** Initialize local list of all files allowed to be shared.
/** Implementation of writeLog
* @param text Text to log
* @param logLevel level of logging
*/ */
private void initFileList() { protected void writeLog(String text, LogLevel logLevel) {
File folder = new File(baseDirectory); logger.writeUDP(text, logLevel);
Vector<String> v = new Vector<String>();
File[] files = folder.listFiles();
/* Add non-recursively files's names to fileList */
for (File f : files) {
if (f.isFile()) {
v.add(f.getName());
}
}
fileList = new String[v.size()];
v.toArray(fileList);
Arrays.sort(fileList);
} }
/** Init sha512 map. /** Implementation of writeLog
* @param e exception to log
* @param logLevel level of logging
*/ */
private void initSha512() { protected void writeLog(Exception e, LogLevel logLevel) {
for(String f: fileList) { logger.writeUDP(e, logLevel);
try {
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
sha512.put(f, md.digest(Files.readAllBytes(Paths.get(baseDirectory + f))));
md.reset();
} catch (NoSuchAlgorithmException e) {
logger.writeUDP("sha512 not supported", LogLevel.Error);
} catch (IOException e) {
logger.writeUDP("cannot read " + f, LogLevel.Warning);
}
}
} }
/** Send an internal error message. /** Create packets
* @param pd ProtocolP2PPacketUDP to respond * @param payload Payload
*/ */
private void sendInternalError(ProtocolP2PPacketUDP pd) { protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
logger.writeUDP("Internal Error", LogLevel.Warning); return (ProtocolP2PPacket<T>)new ProtocolP2PPacketUDP<T>(payload);
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR)));
} catch (Exception e) {
logger.writeUDP(e, LogLevel.Error);
}
} }
/** Send hash response to hash request /** Getter for tracker socket
* @param pd Request received
*/ */
private void sendHashResponse(ProtocolP2PPacketUDP pd) { protected Object getTrackerSocket() {
Payload p = pd.getPayload(); return (Object)tracker.getUDPSocket();
assert p instanceof HashRequest : "payload must be an instance of HashRequest";
if (!(p instanceof HashRequest)) {
sendInternalError(pd);
} else {
String filename = ((HashRequest)p).getFilename();
if (Arrays.binarySearch(fileList, filename) >= 0) {
Map<HashAlgorithm, byte[]> hashes = new HashMap<>();
for (HashAlgorithm h : ((HashRequest)p).getAlgoList()) {
switch (h) {
case SHA512:
hashes.put(h, sha512.get(filename));
break;
case MD5:
default:
hashes.put(h, new byte[0]);
break;
}
}
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP((Payload)(new HashResponse(filename, hashes))));
} catch (Exception e) {
logger.writeUDP(e, LogLevel.Error);
}
} else {
// file not found
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.NOT_FOUND)));
} catch (Exception e) {
logger.writeUDP(e, LogLevel.Error);
}
}
}
} }
} }

@ -1,8 +1,14 @@
package serverP2P; package serverP2P;
import java.util.Scanner;
import serverP2P.ServerManagementUDP; import serverP2P.ServerManagementUDP;
import serverP2P.ServerManagementTCP; import serverP2P.ServerManagementTCP;
import tools.Directories; import tools.Directories;
import tools.Logger; import tools.Logger;
import tools.HostItem;
import tools.ServerPortRange;
import tools.TrackerPortRange;
/** Server only implementation /** Server only implementation
* First argument of main method is port listened by the server, and is mandatory. * First argument of main method is port listened by the server, and is mandatory.
@ -12,38 +18,111 @@ import tools.Logger;
* @version 1.0 * @version 1.0
*/ */
public class ServerP2P { public class ServerP2P {
private int port;
private Directories directories; private Directories directories;
static private final String subdir = "seeded/"; static private final String subdir = "seeded/";
private Logger logger; private Logger logger;
private HostItem tracker;
private HostItem server;
/** Constructor with portStr containing a port number. /** Constructor with portServerstr containing a port number.
* @param portStr String containing port number of listening. * @param hostnameServer binded hostname
* @param portServer binded port
* @param hostnameTracker tracker hostname
* @param portTracker tracker port
*/ */
public ServerP2P(String portStr) { public ServerP2P(String hostnameServer, int portServer, String hostnameTracker, int portTracker) {
port = Integer.valueOf(Integer.parseInt(portStr)); Scanner scanner = new Scanner(System.in);
directories = new Directories("P2P_JAVA_PROJECT_SERVER_" + port); server = new HostItem(hostnameServer, portServer);
tracker = new HostItem(hostnameTracker, portTracker);
directories = new Directories("P2P_JAVA_PROJECT_SERVER_" + portServer);
directories.createSubdir(subdir); directories.createSubdir(subdir);
logger = new Logger(directories.getDataHomeDirectory() + "server.log"); logger = new Logger(directories.getDataHomeDirectory() + "server.log");
System.out.println("Server will listen on port " + port + " and serve files from " + directories.getDataHomeDirectory() + subdir); System.out.println("Server will listen on port " + portServer + " and serve files from " + directories.getDataHomeDirectory() + subdir);
directories.askOpenDataHomeDirectory(subdir); directories.askOpenDataHomeDirectory(subdir, scanner);
scanner.close();
} }
/** Main program entry point /** Main program entry point
* first parameter is port number and is mandatory * first parameter is port number and is mandatory
* to test, run with: java -ea serverP2P.ServerP2P -- <portNumber> * to test, run with: java serverP2P.ServerP2P
* @param args parameters * @param args parameters
*/ */
public static void main(String [] args) { public static void main(String [] args) {
ServerP2P s = new ServerP2P(args[1]); final String defaultHostname = "localhost";
ServerManagementUDP smudp = new ServerManagementUDP(s.directories.getDataHomeDirectory() + subdir, s.port, s.logger); final ServerPortRange serverPortRange = new ServerPortRange();
ServerManagementTCP smtcp = new ServerManagementTCP(s.directories.getDataHomeDirectory() + subdir, s.port, s.logger); final TrackerPortRange trackerPortRange = new TrackerPortRange();
Thread tudp = new Thread(smudp); String hostnameServer = "";
tudp.setName("server UDP P2P-JAVA-PROJECT"); int portServer = 0;
tudp.start(); String hostnameTracker = "";
Thread ttcp = new Thread(smtcp); int portTracker = 0;
ttcp.setName("server TCP P2P-JAVA-PROJECT"); Scanner scanner = new Scanner(System.in);
ttcp.start();
} if ((args.length != 5) && (args.length != 0)){
System.out.println("usage : java serveurP2P.ServeurP2P (interactive) or java serveurP2P.ServeurP2P -- <serveurHOSTNAME> <serveurPORT> <trackerHOSTNAME> <trackerPORT> (" + serverPortRange + " and " + trackerPortRange +")");
System.exit(1);
}
else if(args.length == 5){
hostnameServer = args[1];
portServer = Integer.valueOf(Integer.parseInt(args[2]));
hostnameTracker = args[3];
portTracker = Integer.valueOf(Integer.parseInt(args[4]));
} else {
System.out.println("Server, enter hostname to bind (default = localhost): ");
hostnameServer = scanner.nextLine();
if(hostnameServer.equals("")){
hostnameServer = defaultHostname;
System.out.println("using default hostname : " + hostnameServer);
}
System.out.println("enter port (default = " + serverPortRange.getDefaultPort() + "): ");
String portServerStr = scanner.nextLine();
if(portServerStr.equals("")){
portServer = serverPortRange.getDefaultPort();
System.out.println("using default port : " + portServer);
} else {
portServer = Integer.valueOf(Integer.parseInt(portServerStr));
}
System.out.println("enter hostname of tracker (default = localhost): ");
hostnameTracker = scanner.nextLine();
if(hostnameTracker.equals("")){
hostnameTracker = defaultHostname;
System.out.println("tracker default hostname : " + hostnameTracker);
}
System.out.println("enter tracker's port (default = " + trackerPortRange.getDefaultPort() + "): ");
String portTrackerStr = scanner.nextLine();
if(portTrackerStr.equals("")){
portTracker = serverPortRange.getDefaultPort();
System.out.println("using default port : " + portTracker);
} else {
portTracker = Integer.valueOf(Integer.parseInt(portTrackerStr));
}
}
System.out.println("using hostname : " + hostnameServer);
if(serverPortRange.isPortInRange(portServer)) {
System.out.println("using port : " + portServer);
}
else {
System.out.println("Port not in range. " + serverPortRange);
portServer = serverPortRange.getDefaultPort();
}
System.out.println("tracker hostname : " + hostnameTracker);
if(trackerPortRange.isPortInRange(portTracker)) {
System.out.println("using port : " + portTracker);
}
else {
System.out.println("Port not in range. " + trackerPortRange);
portTracker = trackerPortRange.getDefaultPort();
}
ServerP2P s = new ServerP2P(hostnameServer, portServer, hostnameTracker, portTracker);
ServerManagementUDP smudp = new ServerManagementUDP(s.directories.getDataHomeDirectory() + subdir, s.server, s.tracker, s.logger);
ServerManagementTCP smtcp = new ServerManagementTCP(s.directories.getDataHomeDirectory() + subdir, s.server, s.tracker, s.logger);
Thread tudp = new Thread(smudp);
tudp.setName("server UDP P2P-JAVA-PROJECT");
tudp.start();
Thread ttcp = new Thread(smtcp);
ttcp.setName("server TCP P2P-JAVA-PROJECT");
ttcp.start();
}
} }

@ -1,6 +1,6 @@
package tools; package tools;
import exception.SizeError; import localException.SizeError;
import exception.ProtocolError; import localException.ProtocolError;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.Arrays; import java.util.Arrays;
@ -219,6 +219,39 @@ public class BytesArrayTools {
} }
} }
/** Read string from byte array
* @param array Byte array to read
* @param start start position in byte array
* @param endStr End string delimiter
* @return String read
* @throws InternalError
*/
public static String readString(byte[] array, int start, String endStr) throws InternalError {
boolean failed = false;
try {
int i = start;
while(true) {
int j = i;
for(byte b: endStr.getBytes()) {
if (b != array[j]) {
failed = true;
break;
}
j++;
}
if (failed) {
i++;
failed = false;
} else {
break;
}
}
return readString(array, start, i - start);
} catch(IndexOutOfBoundsException e) {
throw new InternalError();
}
}
/** Write byte Array to byte Array. /** Write byte Array to byte Array.
* @param dst Destination byte Array * @param dst Destination byte Array
* @param src Source byte Array * @param src Source byte Array

@ -70,6 +70,7 @@ public class Directories {
} }
/** Opens dataHomeDirectory if supported. /** Opens dataHomeDirectory if supported.
* @param subdir Subdir to open (optional)
*/ */
private void openDataHomeDirectory(String subdir) { private void openDataHomeDirectory(String subdir) {
String d = dataHomeDirectory; String d = dataHomeDirectory;
@ -93,11 +94,12 @@ public class Directories {
} }
/** Asks the user to choose opening dataHomeDirectory or not. /** Asks the user to choose opening dataHomeDirectory or not.
* @param subdir subdirectory to open (optional)
* @param scanner Scanner to use for reading input
*/ */
public void askOpenDataHomeDirectory(String subdir) { public void askOpenDataHomeDirectory(String subdir, Scanner scanner) {
if (os.equals("Linux") || os.equals("Mac") || os.equals("Mac OS X")) { if (os.equals("Linux") || os.equals("Mac") || os.equals("Mac OS X")) {
System.out.println("Do you want to open this directory? (y/N)"); System.out.println("Do you want to open this directory? (y/N)");
Scanner scanner = new Scanner(System.in);
String resp = scanner.nextLine(); String resp = scanner.nextLine();
if (resp.equals("y") || resp.equals("Y")) { if (resp.equals("y") || resp.equals("Y")) {
System.out.println("Openning"); System.out.println("Openning");

@ -17,6 +17,7 @@ public class HostItem {
private int port; private int port;
private Socket tcpSocket; private Socket tcpSocket;
private DatagramSocket udpSocket; private DatagramSocket udpSocket;
private InetAddress inetAddress;
/** Constructor with hostname and port /** Constructor with hostname and port
* @param hostname Hostname * @param hostname Hostname
@ -35,19 +36,32 @@ public class HostItem {
try { try {
tcpSocket = new Socket(InetAddress.getByName(hostname), port); tcpSocket = new Socket(InetAddress.getByName(hostname), port);
} catch (SocketException e) { } catch (SocketException e) {
System.err.println("Error: No TCP socket available."); System.err.println("getTCPSocket error: No TCP socket available.");
System.exit(-1); System.exit(-1);
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
System.err.println("Error: Unknown host."); System.err.println("getTCPSocket error: Unknown host (" + this + ").");
System.exit(-1); System.exit(-1);
} catch (IOException e) { } catch (IOException e) {
System.err.println("Error: Cannot create TCP socket"); System.err.println("getTCPSocket error: Cannot create TCP socket (" + this + ").");
System.exit(-1); System.exit(-1);
} }
} }
return tcpSocket; return tcpSocket;
} }
/** Get TCP Socket.
* @return TCP Socket
* @throws SocketException
* @throws UnknownHostException
* @throws IOException
*/
public Socket tryGetTCPSocket() throws SocketException, UnknownHostException, IOException {
if (tcpSocket == null) {
tcpSocket = new Socket(InetAddress.getByName(hostname), port);
}
return tcpSocket;
}
/** Closes tcp socket /** Closes tcp socket
*/ */
public void closeTCPSocket() { public void closeTCPSocket() {
@ -70,19 +84,86 @@ public class HostItem {
udpSocket = new DatagramSocket(); udpSocket = new DatagramSocket();
udpSocket.connect(InetAddress.getByName(hostname), port); udpSocket.connect(InetAddress.getByName(hostname), port);
} catch (SocketException e) { } catch (SocketException e) {
System.err.println("Error: No UDP socket available."); System.err.println("getUDPSocket error: No UDP socket available." );
System.exit(-1); System.exit(-1);
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
System.err.println("Error: Unknown host."); System.err.println("getUDPSocket error: Unknown host (" + this + ").");
System.exit(-1); System.exit(-1);
} }
} }
return udpSocket; return udpSocket;
} }
/** Closes udp socket
*/
public void closeUDPSocket() { public void closeUDPSocket() {
if (udpSocket != null) { if (udpSocket != null) {
udpSocket.close(); udpSocket.close();
} }
udpSocket = null; udpSocket = null;
} }
/** Getter for hostname */
public String getHostname() {
return hostname;
}
/** Getter for port */
public int getPort() {
return port;
}
/** To string
* @return String representation
*/
public String toString() {
return getHostname() + " (port " + getPort() + ")";
}
/** Override of equals method
* @param other Object to test equality with
* @return true if equals
*/
public boolean equals(Object other) {
boolean result = false;
if (other instanceof HostItem) {
HostItem that = (HostItem) other;
result = this.getHostname() == that.getHostname() && this.getPort() == that.getPort();
}
return result;
}
/** Override of hashCode method
* @return a hash code for this object.
*/
public int hashCode() {
return hostname.hashCode() ^ port;
}
/** Get InetAddress associated to this HostItem.
* @return InetAddress
*/
public InetAddress getInetAddress() {
if (inetAddress == null) {
try {
inetAddress = InetAddress.getByName(getHostname());
} catch (UnknownHostException e) {
System.err.println("getInetAddress error: Unknown host (" + this + ").");
System.exit(-1);
}
}
return inetAddress;
}
/** Constructor from Socket.
* @param s socket
*/
public HostItem(Socket s) {
tcpSocket = s;
inetAddress = s.getInetAddress();
hostname = inetAddress.getCanonicalHostName();
port = s.getPort();
}
} }

@ -1,55 +0,0 @@
package tools;
import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
import java.util.InputMismatchException;
import tools.HostItem;
/** Helper to get the server list from the user
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class HostList {
/**
* Let the user enter all server and puts it in a list
* @return list of servers
*/
public static List<HostItem> getServList() {
List<HostItem> serverList = new ArrayList<HostItem>();
Scanner scanner = new Scanner(System.in);
String servName;
int port = 0;
do {
System.out.println("Enter hostname of next server: (or \"stop\" when finished, default: localhost).");
servName = scanner.nextLine();
if (servName.equals("")) {
servName = "localhost";
}
if (!servName.equals("stop")) {
boolean err = false;
do {
System.out.println("Enter port for this server");
try {
port = scanner.nextInt();
scanner.nextLine();
if (port > 65535 || port <= 0) {
err = true;
System.out.println("Port number must be in 1-65535 range. Try again.");
} else {
err = false;
}
} catch (InputMismatchException e) {
System.out.println("Invalid number. Try again.");
err = true;
}
} while (err);
serverList.add(new HostItem(servName, port));
}
} while (!servName.equals("stop"));
return serverList;
}
}

@ -17,6 +17,9 @@ import java.sql.Timestamp;
public class Logger { public class Logger {
private Path logFile; private Path logFile;
/** Constructor with logFile.
* @param logFile name of the file to store logs.
*/
public Logger(String logFile) { public Logger(String logFile) {
assert logFile != null : "Logfile name is null"; assert logFile != null : "Logfile name is null";
this.logFile = Paths.get(logFile); this.logFile = Paths.get(logFile);
@ -120,4 +123,5 @@ public class Logger {
e.printStackTrace(); e.printStackTrace();
} }
} }

@ -0,0 +1,49 @@
package tools;
/** Test ports.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class PortRange {
protected int portMax;
protected int portMin;
protected int defaultPort;
protected String type;
/** Port range constructor
* @param portMin minimum port
* @param portMax maximum port
* @param defaultPort default port
* @param type type of range
*/
public PortRange(int portMin, int portMax, int defaultPort, String type) {
this.portMax = portMax;
this.portMin = portMin;
this.defaultPort = defaultPort;
this.type = type;
}
/** test if port given correspond a range : registered ports, can be used without superuser privileges
* @return true if port was valid
*/
public boolean isPortInRange(int port) {
return ((port >= portMin) && (port <= portMax));
}
/** To String
* @return String representation
*/
public String toString() {
return "default " + type + "port: " + defaultPort + "(range: " + portMin + " -> " + portMax + ")";
}
/** Default port getter
* @return default port
*/
public int getDefaultPort() {
return defaultPort;
}
}

@ -0,0 +1,82 @@
package tools;
import tools.LogLevel;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.Payload;
import protocolP2P.RequestResponseCode;
public abstract class ServeErrors {
/** Implementation of writeLog
* @param text Text to log
* @param logLevel level of logging
*/
protected abstract void writeLog(String text, LogLevel logLevel);
/** Implementation of writeLog
* @param e exception to log
* @param logLevel level of logging
*/
protected abstract void writeLog(Exception e, LogLevel logLevel);
/** Create packets
* @param payload Payload
*/
protected abstract < T extends Payload > ProtocolP2PPacket<?> createProtocolP2PPacket(T payload);
/** Send a NotATracker error message.
* @param pd Request received
*/
protected < T extends ProtocolP2PPacket<?> > void sendNotATracker(T pd) {
try {
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.NOT_A_TRACKER)));
} catch (Exception e) {
writeLog(e, LogLevel.Error);
}
}
/** Send an internal error message.
* @param pd Request received
*/
protected < T extends ProtocolP2PPacket<?> > void sendInternalError(T pd) {
writeLog("Internal Error", LogLevel.Warning);
try {
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.INTERNAL_ERROR)));
} catch (Exception e) {
writeLog(e, LogLevel.Error);
}
}
/** Send a not found message.
* @param pd Request received
*/
protected < T extends ProtocolP2PPacket<?> > void sendNotFound(T pd) {
try {
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.NOT_FOUND)));
} catch (Exception e) {
writeLog(e, LogLevel.Error);
}
}
/** Send an empty directory message.
* @param pd Request received
*/
protected < T extends ProtocolP2PPacket<?> > void sendEmptyDirectory(T pd) {
try {
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.EMPTY_DIRECTORY)));
} catch (Exception e) {
writeLog(e, LogLevel.Error);
}
}
/** Send an empty file message.
* @param pd Request received
*/
protected < T extends ProtocolP2PPacket<?> > void sendEmptyFile(T pd) {
try {
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.EMPTY_FILE)));
} catch (Exception e) {
writeLog(e, LogLevel.Error);
}
}
}

@ -0,0 +1,10 @@
package tools;
import tools.PortRange;
public class ServerPortRange extends PortRange {
/** Constructor */
public ServerPortRange() {
super(7000, 7999, 7070, "server");
}
}

@ -0,0 +1,10 @@
package tools;
import tools.PortRange;
public class TrackerPortRange extends PortRange {
/** Constructor */
public TrackerPortRange() {
super(6000, 6999, 6969, "tracker");
}
}

@ -0,0 +1,93 @@
package tracker;
import java.util.Scanner;
import tracker.TrackerManagementTCP;
import tracker.TrackerManagementUDP;
import tools.Directories;
import tools.Logger;
import tools.LogLevel;
import tools.TrackerPortRange;
import tools.HostItem;
/** Tracker implementation
* First argument of main method is port listened by the tracker, and is mandatory.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class Tracker {
private HostItem tracker;
private Directories directories;
private Logger logger;
/** Constructor with portStr containing a port number.
* @param hostname hostname to bind
* @param port port to bind
*/
public Tracker(String hostname, int port) {
tracker = new HostItem(hostname, port);
directories = new Directories("P2P_JAVA_PROJECT_TRACKER_" + port);
logger = new Logger(directories.getDataHomeDirectory() + "tracker.log");
System.out.println("Tracker will listen on port " + port + " and write logs into " + directories.getDataHomeDirectory());
Scanner scanner = new Scanner(System.in);
directories.askOpenDataHomeDirectory(null, scanner);
scanner.close();
}
/** Main program entry point
* first parameter is port number and is mandatory
* to test, run with: java serverP2P.ServerP2P
* @param args parameters
*/
public static void main(String [] args) {
final TrackerPortRange trackerPortRange = new TrackerPortRange();
final String defaultHostname = "localhost";
Scanner scanner = new Scanner(System.in);
String hostname = "";
int port = 0;
Tracker t;
if ((args.length != 3) && (args.length != 0)){
System.out.println("usage : java tracker.Tracker (interactive) or java trackerP2P.trackerP2P -- <hostname> <PORT> (" + trackerPortRange +")");
System.exit(1);
} else if (args.length == 3){
hostname = args[1];
port = Integer.valueOf(Integer.parseInt(args[2]));
} else {
System.out.println("Tracker Server, enter hostname to bind (default = localhost): ");
hostname = scanner.nextLine();
if(hostname.equals("")){
hostname = defaultHostname;
System.out.println("using default hostname : " + hostname);
}
System.out.println("enter port (default = " + trackerPortRange.getDefaultPort() +"): ");
String portStr = scanner.nextLine();
if(portStr.equals("")){
port = trackerPortRange.getDefaultPort();
System.out.println("using default port : " + port);
} else {
port = Integer.valueOf(Integer.parseInt(portStr));
}
}
System.out.println("using hostname : " + hostname);
if(trackerPortRange.isPortInRange(port)) {
System.out.println("using port : " + port);
t = new Tracker(hostname, port);
}
else {
System.out.println("Port not in range. " + trackerPortRange);
t = new Tracker(hostname, trackerPortRange.getDefaultPort());
}
TrackerManagementUDP tmudp = new TrackerManagementUDP(t.tracker, t.logger);
TrackerManagementTCP tmtcp = new TrackerManagementTCP(t.tracker, t.logger);
Thread tudp = new Thread(tmudp);
tudp.setName("Tracker UDP P2P-JAVA-PROJECT");
tudp.start();
Thread ttcp = new Thread(tmtcp);
ttcp.setName("Tracker TCP P2P-JAVA-PROJECT");
ttcp.start();
}
}

@ -0,0 +1,201 @@
package tracker;
import tools.ServeErrors;
import tools.HostItem;
import tools.Logger;
import tools.LogLevel;
import java.util.Map;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.Payload;
import protocolP2P.DiscoverRequest;
import protocolP2P.DiscoverResponse;
import protocolP2P.FileList;
import protocolP2P.Unregister;
import protocolP2P.Register;
import protocolP2P.RequestResponseCode;
import localException.InternalError;
import remoteException.EmptyDirectory;
import exception.LocalException;
/** Tracker management implementation
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public abstract class TrackerManagement extends ServeErrors implements Runnable {
protected HostItem tracker;
protected Logger logger;
protected List<HostItem> hostList = new ArrayList<>();
protected Map<String, List<HostItem>> fileList = new HashMap<>();
protected volatile boolean stop;
/** Constructor
* @param tracker Tracker HostItem
* @param logger Logger
*/
public TrackerManagement(HostItem tracker, Logger logger) {
stop = false;
this.tracker = tracker;
this.logger = logger;
}
/** Handle Discover request
* @param pd Received request
* @throws InternalError
*/
protected < T extends ProtocolP2PPacket<?> > void handleDiscover(T pd) throws InternalError {
Payload p = pd.getPayload();
assert p instanceof DiscoverRequest : "payload must be an instance of DiscoverRequest";
if (!(p instanceof DiscoverRequest)) {
sendInternalError(pd);
} else {
String filename = ((DiscoverRequest)p).getFilename();
try {
pd.sendResponse(createProtocolP2PPacket(new DiscoverResponse(filename, fileList.getOrDefault(filename, hostList))));
} catch (Exception e) {
writeLog(e, LogLevel.Error);
}
}
}
/** Handle List Responses
* @param pd Received response
* @throws InternalError
*/
protected <T extends ProtocolP2PPacket<?> > void handleListResponse(T pd, HostItem host) throws InternalError {
Payload p = pd.getPayload();
assert p instanceof FileList: "payload must be an instance of FileList";
if (!(p instanceof FileList)) {
throw new InternalError();
} else {
String[] f = ((FileList)p).getFileList();
for (String file: f) {
List<HostItem> h = fileList.get(file);
if (h != null) {
if (!h.contains(host)) {
h.add(host);
}
} else {
List<HostItem> emptyH = new ArrayList<>();
emptyH.add(host);
fileList.put(file, emptyH);
}
}
}
}
/** Handle Unregistering
* @param pd Request received
* @throws InternalError
*/
protected < T extends ProtocolP2PPacket<?> > void handleUnregister(T pd) throws InternalError {
Payload p = pd.getPayload();
assert p instanceof Unregister : "payload must be an instance of Unregister";
if (!(p instanceof Unregister)) {
sendInternalError(pd);
throw new InternalError();
}
HostItem host = ((Unregister)p).getHostItem();
writeLog("Received UNREGISTER from host " + pd.getHostItem() + ". Removing host " + host, LogLevel.Action);
hostList.remove(host);
for(String f: fileList.keySet()) {
fileList.get(f).remove(host);
if(fileList.get(f).isEmpty()) {
fileList.remove(f);
}
}
}
/** Getter for HostItem socket
* @param hostItem HostItem
*/
protected abstract Object getHostItemSocket(HostItem hostItem);
/** Close HostItem socket
* @param hostItem HostItem
*/
protected abstract void closeHostItemSocket(HostItem hostItem);
/** Handle Registering
* @param pd Received request
* @throws InternalError
*/
protected < T extends ProtocolP2PPacket<?> > void handleRegister(T pd) throws InternalError {
Payload p = pd.getPayload();
assert p instanceof Register : "payload must be an instance of Register";
if (!(p instanceof Register)) {
throw new InternalError();
}
// add host to known host list
HostItem host = ((Register)p).getHostItem();
if (!hostList.contains(host)) {
hostList.add(host);
}
// send a list request
try {
ProtocolP2PPacket<?> pLReq = createProtocolP2PPacket(new Payload(RequestResponseCode.LIST_REQUEST));
pLReq.sendRequest(getHostItemSocket(host));
writeLog("Received REGISTER from host " + pd.getHostItem() + ". Adding host " + host + " to list. Sending List request", LogLevel.Action);
handleListResponse(pLReq.receiveResponse(), host);
writeLog("Received LIST RESPONSE from host " + pd.getHostItem(), LogLevel.Action);
closeHostItemSocket(host);
} catch (EmptyDirectory e) {
writeLog("Empty Directory", LogLevel.Debug);
hostList.remove(host);
writeLog("Received EMPTY DIRECTORY from host " + pd.getHostItem() + ". Aborting.", LogLevel.Action);
} catch (Exception e) {
// remove from list because list request could not be send
hostList.remove(host);
writeLog("Aborting the add of host " + host, LogLevel.Action);
writeLog(e, LogLevel.Error);
}
}
/** Handle requests
* @throws LocalException
*/
protected <T extends ProtocolP2PPacket<?> > void handleRequest(T pd) throws LocalException {
Payload p = pd.getPayload();
switch (p.getRequestResponseCode()) {
case LOAD_REQUEST:
writeLog("Received LOAD_REQUEST from host " + pd.getHostItem() + ", sending NOT_FOUND", LogLevel.Action);
sendNotFound(pd);
break;
case LIST_REQUEST:
writeLog("Received LIST_REQUEST from host " + pd.getHostItem() + ", sending EMPTY_DIRECTORY", LogLevel.Action);
sendEmptyDirectory(pd);
break;
case HASH_REQUEST:
writeLog("Received HASH_REQUEST from host " + pd.getHostItem() + ", sending NOT_FOUND", LogLevel.Action);
sendNotFound(pd);
break;
case REGISTER:
writeLog("Received REGISTER from host " + pd.getHostItem(), LogLevel.Debug);
handleRegister(pd);
break;
case UNREGISTER:
writeLog("Received UNREGISTER from host " + pd.getHostItem(), LogLevel.Debug);
handleUnregister(pd);
break;
case DISCOVER_REQUEST:
writeLog("Received DISCOVER REQUEST from host " + pd.getHostItem(), LogLevel.Action);
handleDiscover(pd);
break;
default:
writeLog("Received grabbage from host " + pd.getHostItem(), LogLevel.Action);
sendInternalError(pd);
break;
}
}
/** Stop the thread */
public void setStop() {
stop = true;
}
}

@ -0,0 +1,155 @@
package tracker;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.HashMap;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.net.InetAddress;
import protocolP2P.ProtocolP2PPacketTCP;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.RequestResponseCode;
import protocolP2P.Payload;
import protocolP2P.Register;
import protocolP2P.Unregister;
import protocolP2P.DiscoverRequest;
import protocolP2P.DiscoverResponse;
import protocolP2P.FileList;
import exception.LocalException;
import remoteException.EmptyDirectory;
import localException.InternalError;
import localException.SocketClosed;
import tracker.TrackerManagement;
import tools.HostItem;
import tools.Logger;
import tools.LogLevel;
/** Tracker management implementation with tcp
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class TrackerManagementTCP extends TrackerManagement {
private ServerSocket socket;
/** Constructor with port and logger.
* @param tracker hostitem of the tracker.
* @param logger Logger object
*/
public TrackerManagementTCP(HostItem tracker, Logger logger) {
super(tracker, logger);
try {
socket = new ServerSocket(tracker.getPort(), 10, tracker.getInetAddress());
} catch (SocketException e) {
writeLog("Error: cannot listen on" + tracker, LogLevel.Error);
System.exit(-1);
} catch (IOException e) {
writeLog("Error: cannot open socket", LogLevel.Error);
System.exit(-2);
}
}
/** Implementation of runnable. This methods allows to run the server.
*/
public void run() {
writeLog("Tracker sucessfully started", LogLevel.Info);
while (!stop) {
try {
Socket s = socket.accept();
ClientHandler c = new ClientHandler(s);
(new Thread(c)).start();
} catch (IOException e) {
writeLog("Error while accepting new connection", LogLevel.Warning);
}
}
}
/** Private runnable class allowing to serve one client.
*/
private class ClientHandler implements Runnable {
private HostItem addr;
/** Constructor with a socket.
* @param s Socket of this client
*/
public ClientHandler(Socket s) {
this.addr = new HostItem(s);
}
/** Implementation of runnable. This method allow to serve one client.
*/
public void run() {
boolean end = false;
writeLog("[ " + addr + "] New connection", LogLevel.Action);
do {
end = handleClientRequest();
} while(!end);
writeLog("[ " + addr + "] End of connection", LogLevel.Action);
}
/** Respond to next request incomming on socket s.
* @param s Socket used to read request and send response
* @return true if cannot expect another request (ie, socket is closed)
*/
private boolean handleClientRequest() {
try {
ProtocolP2PPacketTCP<?> pd = new ProtocolP2PPacketTCP<>((Object)addr.getTCPSocket());
handleRequest(pd);
} catch (IOException e) {
writeLog(e, LogLevel.Warning);
return true;
} catch (SocketClosed e) {
return true;
} catch (LocalException e) {
writeLog(e, LogLevel.Warning);
return true;
}
return false;
}
}
/** Implementation of writeLog
* @param text Text to log
* @param logLevel level of logging
*/
protected void writeLog(String text, LogLevel logLevel) {
logger.writeTCP(text, logLevel);
}
/** Implementation of writeLog
* @param e exception to log
* @param logLevel level of logging
*/
protected void writeLog(Exception e, LogLevel logLevel) {
logger.writeTCP(e, logLevel);
}
/** Create packets
* @param payload Payload
*/
protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketTCP<T>(payload);
}
/** Getter for HostItem socket
* @param hostItem HostItem
*/
protected Object getHostItemSocket(HostItem hostItem) {
return (Object)hostItem.getTCPSocket();
}
/** Close HostItem socket
* @param hostItem HostItem
*/
protected void closeHostItemSocket(HostItem hostItem) {
hostItem.closeTCPSocket();
}
}

@ -0,0 +1,103 @@
package tracker;
import tools.Logger;
import tools.LogLevel;
import java.net.DatagramSocket;
import protocolP2P.ProtocolP2PPacketUDP;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.RequestResponseCode;
import protocolP2P.Payload;
import protocolP2P.Register;
import protocolP2P.Unregister;
import tools.HostItem;
import java.util.ArrayList;
import java.util.List;
import java.io.IOException;
import java.net.SocketException;
import exception.LocalException;
import java.util.Map;
import java.util.HashMap;
import protocolP2P.DiscoverRequest;
import protocolP2P.DiscoverResponse;
import protocolP2P.FileList;
import localException.InternalError;
import remoteException.EmptyDirectory;
import java.net.InetAddress;
import java.net.UnknownHostException;
import tracker.TrackerManagement;
/** Tracker management implementation with udp
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class TrackerManagementUDP extends TrackerManagement {
private DatagramSocket socket;
/** Constructor with port and logger.
* @param tracker hostitem of the tracker.
* @param logger Logger object
*/
public TrackerManagementUDP(HostItem tracker, Logger logger) {
super(tracker, logger);
try {
socket = new DatagramSocket(tracker.getPort(), tracker.getInetAddress());
} catch (SocketException e) {
logger.writeUDP("Error: cannot listen on " + tracker, LogLevel.Error);
System.exit(-1);
}
}
/** Implementation of runnable. This methods allows to run the tracker.
*/
public void run() {
logger.writeUDP("Tracker successfully started", LogLevel.Info);
while(!stop) {
try {
ProtocolP2PPacketUDP<?> pd = new ProtocolP2PPacketUDP<>((Object)socket);
handleRequest(pd);
} catch (IOException e) {
logger.writeUDP(e, LogLevel.Warning);
} catch (LocalException e) {
logger.writeUDP(e, LogLevel.Warning);
}
}
}
/** Implementation of writeLog
* @param text Text to log
* @param logLevel level of logging
*/
protected void writeLog(String text, LogLevel logLevel) {
logger.writeUDP(text, logLevel);
}
/** Implementation of writeLog
* @param e exception to log
* @param logLevel level of logging
*/
protected void writeLog(Exception e, LogLevel logLevel) {
logger.writeUDP(e, logLevel);
}
/** Create packets
* @param payload Payload
*/
protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketUDP<T>(payload);
}
/** Getter for HostItem socket
* @param hostItem HostItem
*/
protected Object getHostItemSocket(HostItem hostItem) {
return (Object)hostItem.getUDPSocket();
}
/** Close HostItem socket
* @param hostItem HostItem
*/
protected void closeHostItemSocket(HostItem hostItem) {
hostItem.closeUDPSocket();
}
}
Loading…
Cancel
Save