Merge pull request 'Étape 4' (#46) from etape4 into master
All checks were successful
flavien's git/Projet_JAVA_P2P_STRI2A/pipeline/head This commit looks good
All checks were successful
flavien's git/Projet_JAVA_P2P_STRI2A/pipeline/head This commit looks good
This commit is contained in:
commit
07cecf2fab
5
Jenkinsfile
vendored
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
|
||||||
|
|||||||
1
doc/tracker_sequence_diagram.drawio
Normal file
1
doc/tracker_sequence_diagram.drawio
Normal file
@ -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>
|
||||||
3
doc/tracker_sequence_diagram.svg
Normal file
3
doc/tracker_sequence_diagram.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 29 KiB |
409
src/clientP2P/ClientDownload.java
Normal file
409
src/clientP2P/ClientDownload.java
Normal file
@ -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);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
310
src/clientP2P/ClientDownloadPart.java
Normal file
310
src/clientP2P/ClientDownloadPart.java
Normal file
@ -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
|
|
||||||
|
/** Get the socket */
|
||||||
|
protected Object getSocket() {
|
||||||
|
return (Object) socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Close the socket
|
||||||
*/
|
*/
|
||||||
public boolean hasFailed() {
|
protected void closeSocket() throws IOException {
|
||||||
return failed;
|
socket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Asks to stop thread.
|
/** Implementation of writeLog
|
||||||
* @throws InterruptedException
|
* @param text Text to log
|
||||||
|
* @param logLevel level of logging
|
||||||
*/
|
*/
|
||||||
public synchronized void setStop() throws InterruptedException {
|
protected void writeLog(String text, LogLevel logLevel) {
|
||||||
stop = true;
|
logger.writeTCP(text, logLevel);
|
||||||
this.notifyAll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Runnable implementation */
|
/** Implementation of writeLog
|
||||||
public void run() {
|
* @param e exception to log
|
||||||
while(!stop) {
|
* @param logLevel level of logging
|
||||||
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.
|
|
||||||
* @return list of offsets
|
|
||||||
*/
|
*/
|
||||||
public List<Long> getFailed() {
|
protected void writeLog(Exception e, LogLevel logLevel) {
|
||||||
List<Long> ret = new ArrayList<>();
|
logger.writeTCP(e, logLevel);
|
||||||
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
|
/** Create packets
|
||||||
* @throws InterruptedException
|
* @param payload Payload
|
||||||
*/
|
*/
|
||||||
public List<Long> getDone() throws InterruptedException {
|
protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
|
||||||
if (tasksDone.size() == 0) {
|
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketTCP<T>(payload);
|
||||||
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);
|
|
||||||
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).
|
|
||||||
* @param d request packet
|
|
||||||
* @return true on failure, else false
|
|
||||||
*/
|
|
||||||
public boolean downloadPart(ProtocolP2PPacketTCP 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;
|
|
||||||
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Close the socket
|
||||||
*/
|
*/
|
||||||
public boolean hasFailed() {
|
protected void closeSocket() throws IOException {
|
||||||
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");
|
|
||||||
socket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get list of offsets that have not be downloaded if failed, else
|
|
||||||
* empty list.
|
/** Implementation of writeLog
|
||||||
* @return list of offsets
|
* @param text Text to log
|
||||||
|
* @param logLevel level of logging
|
||||||
*/
|
*/
|
||||||
public List<Long> getFailed() {
|
protected void writeLog(String text, LogLevel logLevel) {
|
||||||
List<Long> ret = new ArrayList<>();
|
logger.writeUDP(text, logLevel);
|
||||||
if (failed) {
|
|
||||||
ret.addAll(pendingTasks);
|
|
||||||
ret.addAll(toDoTasks);
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get list of downloaded file parts offset, then clear this list.
|
/** Implementation of writeLog
|
||||||
* @return list of offsets
|
* @param e exception to log
|
||||||
* @throws InterruptedException
|
* @param logLevel level of logging
|
||||||
*/
|
*/
|
||||||
public List<Long> getDone() throws InterruptedException {
|
protected void writeLog(Exception e, LogLevel logLevel) {
|
||||||
if (tasksDone.size() == 0) {
|
logger.writeUDP(e, logLevel);
|
||||||
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.
|
/** Create packets
|
||||||
* @param task offset to download
|
* @param payload Payload
|
||||||
* @throws InterruptedException
|
|
||||||
*/
|
*/
|
||||||
public synchronized void assignTask(Long task) throws InterruptedException {
|
protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
|
||||||
synchronized(this) {
|
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketUDP<T>(payload);
|
||||||
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);
|
|
||||||
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.
|
|
||||||
* @param offset Offset of the file part to download
|
|
||||||
* @return ProtocolP2PPacketTCP used to send request
|
|
||||||
*/
|
|
||||||
private ProtocolP2PPacketUDP 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 {
|
|
||||||
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
|
/** Create a clientDownloadPart
|
||||||
|
* @param filename name of the file to download
|
||||||
|
* @param hostItem Hostitem of the server
|
||||||
*/
|
*/
|
||||||
public void setStop() {
|
protected ClientDownloadPart createDownloadPart(String filename, HostItem hostItem) {
|
||||||
stop = true;
|
return (ClientDownloadPart)new ClientDownloadPartTCP((ClientDownload)this, filename, hostItem.getTCPSocket(), partsSubdir, logger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Close HostItem socket
|
||||||
|
* @param hostItem HostItem
|
||||||
|
*/
|
||||||
|
protected void closeHostItemSocket(HostItem hostItem) {
|
||||||
|
hostItem.closeTCPSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Runnable implementation
|
/** Implementation of writeLog
|
||||||
|
* @param text Text to log
|
||||||
|
* @param logLevel level of logging
|
||||||
*/
|
*/
|
||||||
public void run() {
|
protected void writeLog(String text, LogLevel logLevel) {
|
||||||
try {
|
logger.writeTCP(text, logLevel);
|
||||||
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.
|
/** Implementation of writeLog
|
||||||
|
* @param e exception to log
|
||||||
|
* @param logLevel level of logging
|
||||||
*/
|
*/
|
||||||
private void initThreads() {
|
protected void writeLog(Exception e, LogLevel logLevel) {
|
||||||
for(HostItem hostItem: hostList) {
|
logger.writeTCP(e, logLevel);
|
||||||
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.
|
/** Create packets
|
||||||
* @throws InternalError
|
* @param payload Payload
|
||||||
*/
|
*/
|
||||||
private void checkTasksStatus() throws InternalError {
|
protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
|
||||||
try {
|
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketTCP<T>(payload);
|
||||||
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.
|
/** Getter for HostItem socket
|
||||||
* @throws InternalError
|
* @param hostItem HostItem
|
||||||
*/
|
*/
|
||||||
private void assignTasks() throws InternalError {
|
protected Object getHostItemSocket(HostItem hostItem) {
|
||||||
Random rand = new Random();
|
return (Object)hostItem.getTCPSocket();
|
||||||
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.
|
|
||||||
* This is done by comparing hash512sum.
|
|
||||||
* @throws InternalError
|
|
||||||
*/
|
|
||||||
private 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);
|
|
||||||
}
|
|
||||||
System.err.println("Host list purge: done");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Getter for hash512sum
|
|
||||||
* @return hash512sum
|
|
||||||
*/
|
|
||||||
public byte[] getHashSum512() {
|
|
||||||
return hash512;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Initialize infos about file to download (size, hash512sum, partslist to dl).
|
|
||||||
* Also download first partfile (to get size).
|
|
||||||
* @throws InternalError
|
|
||||||
*/
|
|
||||||
private void init() throws InternalError {
|
|
||||||
// get size
|
|
||||||
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.
|
|
||||||
* @throws InternalError
|
|
||||||
*/
|
|
||||||
private void setSize() throws InternalError {
|
|
||||||
ProtocolP2PPacketTCP d = new ProtocolP2PPacketTCP((Payload) new LoadRequest(filename, 0, MAX_PARTIAL_SIZE));
|
|
||||||
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.
|
|
||||||
* @return true when file have successfully been reassembled.
|
|
||||||
*/
|
|
||||||
public boolean getSuccess() {
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reassemble file from file parts.
|
|
||||||
* Set success to true if file is reassembled successfully.
|
|
||||||
*/
|
|
||||||
private void reassembleFile() {
|
|
||||||
boolean firstPart = true;
|
|
||||||
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
|
/** Create a clientDownloadPart
|
||||||
|
* @param filename name of the file to download
|
||||||
|
* @param hostItem Hostitem of the server
|
||||||
*/
|
*/
|
||||||
public void setStop() {
|
protected ClientDownloadPart createDownloadPart(String filename, HostItem hostItem) {
|
||||||
stop = true;
|
return (ClientDownloadPart)new ClientDownloadPartUDP((ClientDownload)this, filename, hostItem.getUDPSocket(), partsSubdir, logger);
|
||||||
}
|
|
||||||
|
|
||||||
/** 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.
|
/** Implementation of writeLog
|
||||||
|
* @param text Text to log
|
||||||
|
* @param logLevel level of logging
|
||||||
*/
|
*/
|
||||||
private void initThreads() {
|
protected void writeLog(String text, LogLevel logLevel) {
|
||||||
for(HostItem hostItem: hostList) {
|
logger.writeUDP(text, logLevel);
|
||||||
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.
|
/** Implementation of writeLog
|
||||||
* @throws InternalError
|
* @param e exception to log
|
||||||
|
* @param logLevel level of logging
|
||||||
*/
|
*/
|
||||||
private void checkTasksStatus() throws InternalError {
|
protected void writeLog(Exception e, LogLevel logLevel) {
|
||||||
try {
|
logger.writeUDP(e, logLevel);
|
||||||
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.
|
/** Create packets
|
||||||
* @throws InternalError
|
* @param payload Payload
|
||||||
*/
|
*/
|
||||||
private void assignTasks() throws InternalError {
|
protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
|
||||||
Random rand = new Random();
|
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketUDP<T>(payload);
|
||||||
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 */
|
/** Getter for HostItem socket
|
||||||
private void stopTasks() {
|
* @param hostItem HostItem
|
||||||
for(ClientDownloadPartUDP c : sockList) {
|
*/
|
||||||
try {
|
protected Object getHostItemSocket(HostItem hostItem) {
|
||||||
c.setStop();
|
return (Object)hostItem.getUDPSocket();
|
||||||
} catch (InterruptedException e) {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Get hashsum from server.
|
/** Close HostItem socket
|
||||||
* @param hostItem server to ask hash
|
* @param hostItem HostItem
|
||||||
* @return hash512sum
|
|
||||||
* @throws InternalError
|
|
||||||
*/
|
*/
|
||||||
private byte[] getHashSum512(HostItem hostItem) throws InternalError {
|
protected void closeHostItemSocket(HostItem hostItem) {
|
||||||
byte[] hash;
|
hostItem.closeUDPSocket();
|
||||||
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.
|
|
||||||
* This is done by comparing hash512sum.
|
|
||||||
* @throws InternalError
|
|
||||||
*/
|
|
||||||
private 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);
|
|
||||||
}
|
|
||||||
System.err.println("Host list purge: done");
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Getter for hash512sum
|
|
||||||
* @return hash512sum
|
|
||||||
*/
|
|
||||||
public byte[] getHashSum512() {
|
|
||||||
return hash512;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Initialize infos about file to download (size, hash512sum, partslist to dl).
|
|
||||||
* Also download first partfile (to get size).
|
|
||||||
* @throws InternalError
|
|
||||||
*/
|
|
||||||
private void init() throws InternalError {
|
|
||||||
// get size
|
|
||||||
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.
|
|
||||||
* @throws InternalError
|
|
||||||
*/
|
|
||||||
private void setSize() throws InternalError {
|
|
||||||
ProtocolP2PPacketUDP d = new ProtocolP2PPacketUDP((Payload) new LoadRequest(filename, 0, MAX_PARTIAL_SIZE));
|
|
||||||
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.
|
|
||||||
* @return true when file have successfully been reassembled.
|
|
||||||
*/
|
|
||||||
public boolean getSuccess() {
|
|
||||||
return success;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Reassemble file from file parts.
|
|
||||||
* Set success to true if file is reassembled successfully.
|
|
||||||
*/
|
|
||||||
private void reassembleFile() {
|
|
||||||
boolean firstPart = true;
|
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
262
src/clientP2P/ClientManagement.java
Normal file
262
src/clientP2P/ClientManagement.java
Normal file
@ -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 server’s 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 server’s response", LogLevel.Error);
|
||||||
|
} catch (VersionError e) {
|
||||||
|
writeLog("Server’s 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 client’s 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, List<HostItem> hostList, String partsSubdir) {
|
public ClientManagementTCP(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, Scanner scanner) {
|
||||||
this.baseDirectory = baseDirectory;
|
super(baseDirectory, tracker, partsSubdir, logger, scanner);
|
||||||
this.hostList = hostList;
|
|
||||||
this.partsSubdir = partsSubdir;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Implementation of Runnable
|
/** Initialize downloader
|
||||||
|
* @param filename Name of the file to download
|
||||||
*/
|
*/
|
||||||
public void run() {
|
protected void initDownloader(String filename) {
|
||||||
try {
|
downLoader = (ClientDownload) new ClientDownloadTCP(filename, hostList, partsSubdir, baseDirectory, logger);
|
||||||
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) {
|
/** Implementation of writeLog
|
||||||
System.out.println(listItem);
|
* @param text Text to log
|
||||||
}
|
* @param logLevel level of logging
|
||||||
System.out.println("Name of the file to download:");
|
*/
|
||||||
String f = scanner.nextLine();
|
protected void writeLog(String text, LogLevel logLevel) {
|
||||||
download(f);
|
logger.writeTCP(text, logLevel);
|
||||||
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 server’s response");
|
|
||||||
} catch (VersionError e) {
|
|
||||||
System.err.println("Error: Server’s 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 client’s 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
|
/** Implementation of writeLog
|
||||||
* @param filename name of the file to download
|
* @param e exception to log
|
||||||
* @throws NotFound
|
* @param logLevel level of logging
|
||||||
* @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 void writeLog(Exception e, LogLevel logLevel) {
|
||||||
ClientDownloadTCP downLoader = new ClientDownloadTCP(filename, hostList, partsSubdir, baseDirectory);
|
logger.writeTCP(e, logLevel);
|
||||||
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 server’s directory content
|
/** Create packets
|
||||||
* @return list of files
|
* @param payload Payload
|
||||||
* @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 < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
|
||||||
ProtocolP2PPacketTCP d = new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.LIST_REQUEST));
|
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketTCP<T>(payload);
|
||||||
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 tracker socket
|
||||||
* @param filename
|
|
||||||
* @return hashsum
|
|
||||||
*/
|
*/
|
||||||
private byte[] computeHashsum(String filename, HashAlgorithm h) {
|
protected Object getTrackerSocket() {
|
||||||
try {
|
return (Object)tracker.getTCPSocket();
|
||||||
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
|
}
|
||||||
return md.digest(Files.readAllBytes(Paths.get(baseDirectory + filename)));
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
/** Getter for HostItem socket
|
||||||
System.out.println("Error: " + h.getName() + " not supported");
|
* @param hostItem HostItem
|
||||||
} catch (IOException e) {
|
*/
|
||||||
System.out.println("Error: cannot read " + filename);
|
protected Object getHostItemSocket(HostItem hostItem) {
|
||||||
}
|
return (Object)hostItem.getTCPSocket();
|
||||||
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, List<HostItem> hostList, String partsSubdir) {
|
public ClientManagementUDP(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, Scanner scanner) {
|
||||||
this.baseDirectory = baseDirectory;
|
super(baseDirectory, tracker, partsSubdir, logger, scanner);
|
||||||
this.hostList = hostList;
|
|
||||||
this.partsSubdir = partsSubdir;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Implementation of Runnable
|
/** Initialize downloader
|
||||||
|
* @param filename Name of the file to download
|
||||||
*/
|
*/
|
||||||
public void run() {
|
protected void initDownloader(String filename) {
|
||||||
try {
|
downLoader = (ClientDownload) new ClientDownloadUDP(filename, hostList, partsSubdir, baseDirectory, logger);
|
||||||
System.out.println("Enter all servers: type \"stop\" when finished");
|
}
|
||||||
Scanner scanner = new Scanner(System.in);
|
|
||||||
String[] list = listDirectory();
|
/** Implementation of writeLog
|
||||||
System.out.println("Files present on the server:");
|
* @param text Text to log
|
||||||
for(String listItem: list) {
|
* @param logLevel level of logging
|
||||||
System.out.println(listItem);
|
*/
|
||||||
}
|
protected void writeLog(String text, LogLevel logLevel) {
|
||||||
System.out.println("Name of the file to download:");
|
logger.writeUDP(text, logLevel);
|
||||||
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 server’s response");
|
|
||||||
} catch (VersionError e) {
|
|
||||||
System.err.println("Error: Server’s 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 client’s 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
|
/** Implementation of writeLog
|
||||||
* @param filename name of the file to download
|
* @param e exception to log
|
||||||
* @throws NotFound
|
* @param logLevel level of logging
|
||||||
* @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 void writeLog(Exception e, LogLevel logLevel) {
|
||||||
ClientDownloadUDP downLoader = new ClientDownloadUDP(filename, hostList, partsSubdir, baseDirectory);
|
logger.writeUDP(e, logLevel);
|
||||||
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 server’s directory content
|
/** Create packets
|
||||||
* @return list of files
|
* @param payload Payload
|
||||||
* @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 < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
|
||||||
ProtocolP2PPacketUDP d = new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.LIST_REQUEST));
|
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketUDP<T>(payload);
|
||||||
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.
|
/** Getter for tracker socket
|
||||||
* @param filename
|
|
||||||
* @return hashsum
|
|
||||||
*/
|
*/
|
||||||
private byte[] computeHashsum(String filename, HashAlgorithm h) {
|
protected Object getTrackerSocket() {
|
||||||
try {
|
return (Object)tracker.getUDPSocket();
|
||||||
MessageDigest md = MessageDigest.getInstance(HashAlgorithm.SHA512.getName());
|
}
|
||||||
return md.digest(Files.readAllBytes(Paths.get(baseDirectory + filename)));
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
/** Getter for HostItem socket
|
||||||
System.out.println("Error: " + h.getName() + " not supported");
|
* @param hostItem HostItem
|
||||||
} catch (IOException e) {
|
*/
|
||||||
System.out.println("Error: cannot read " + filename);
|
protected Object getHostItemSocket(HostItem hostItem) {
|
||||||
}
|
return (Object)hostItem.getUDPSocket();
|
||||||
return new byte[0];
|
}
|
||||||
|
|
||||||
|
/** Close HostItem socket
|
||||||
|
* @param hostItem HostItem
|
||||||
|
*/
|
||||||
|
protected void closeHostItemSocket(HostItem hostItem) {
|
||||||
|
hostItem.closeUDPSocket();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Server threads
|
System.out.println("using hostname : " + hostnameServer);
|
||||||
ServerManagementUDP smudp = new ServerManagementUDP(c.directories.getDataHomeDirectory() + subdir, c.port, c.logger);
|
if(serverPortRange.isPortInRange(portServer)){
|
||||||
ServerManagementTCP smtcp = new ServerManagementTCP(c.directories.getDataHomeDirectory() + subdir, c.port, c.logger);
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
|
||||||
}
|
|
||||||
7
src/localException/InternalError.java
Normal file
7
src/localException/InternalError.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package localException;
|
||||||
|
|
||||||
|
import exception.LocalException;
|
||||||
|
|
||||||
|
public class InternalError extends LocalException {
|
||||||
|
private static final long serialVersionUID = 12L;
|
||||||
|
}
|
||||||
7
src/localException/ProtocolError.java
Normal file
7
src/localException/ProtocolError.java
Normal file
@ -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;
|
||||||
}
|
}
|
||||||
7
src/localException/SocketClosed.java
Normal file
7
src/localException/SocketClosed.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package localException;
|
||||||
|
|
||||||
|
import exception.LocalException;
|
||||||
|
|
||||||
|
public class SocketClosed extends LocalException {
|
||||||
|
private static final long serialVersionUID = 12L;
|
||||||
|
}
|
||||||
7
src/localException/TransmissionError.java
Normal file
7
src/localException/TransmissionError.java
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package localException;
|
||||||
|
|
||||||
|
import exception.LocalException;
|
||||||
|
|
||||||
|
public class TransmissionError extends LocalException {
|
||||||
|
private static final long serialVersionUID = 12L;
|
||||||
|
}
|
||||||
7
src/localException/VersionError.java
Normal file
7
src/localException/VersionError.java
Normal file
@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
71
src/protocolP2P/DiscoverRequest.java
Normal file
71
src/protocolP2P/DiscoverRequest.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
117
src/protocolP2P/DiscoverResponse.java
Normal file
117
src/protocolP2P/DiscoverResponse.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
77
src/protocolP2P/Register.java
Normal file
77
src/protocolP2P/Register.java
Normal file
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
77
src/protocolP2P/Unregister.java
Normal file
77
src/protocolP2P/Unregister.java
Normal file
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
7
src/remoteException/NotATracker.java
Normal file
7
src/remoteException/NotATracker.java
Normal file
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
156
src/serverP2P/FileWatcher.java
Normal file
156
src/serverP2P/FileWatcher.java
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
65
src/serverP2P/FileWatcherTCP.java
Normal file
65
src/serverP2P/FileWatcherTCP.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
64
src/serverP2P/FileWatcherUDP.java
Normal file
64
src/serverP2P/FileWatcherUDP.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
234
src/serverP2P/ServerManagement.java
Normal file
234
src/serverP2P/ServerManagement.java
Normal file
@ -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.
|
|
||||||
* @param pd ProtocolP2PPacketUDP to respond
|
|
||||||
*/
|
|
||||||
private void sendInternalError(ProtocolP2PPacketUDP pd) {
|
|
||||||
logger.writeUDP("Internal Error", LogLevel.Warning);
|
|
||||||
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
|
/** Create packets
|
||||||
* @param pd Request received
|
* @param payload Payload
|
||||||
*/
|
*/
|
||||||
private void sendHashResponse(ProtocolP2PPacketUDP pd) {
|
protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
|
||||||
Payload p = pd.getPayload();
|
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketUDP<T>(payload);
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Getter for tracker socket
|
||||||
|
*/
|
||||||
|
protected Object getTrackerSocket() {
|
||||||
|
return (Object)tracker.getUDPSocket();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
49
src/tools/PortRange.java
Normal file
49
src/tools/PortRange.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
82
src/tools/ServeErrors.java
Normal file
82
src/tools/ServeErrors.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
10
src/tools/ServerPortRange.java
Normal file
10
src/tools/ServerPortRange.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package tools;
|
||||||
|
import tools.PortRange;
|
||||||
|
|
||||||
|
public class ServerPortRange extends PortRange {
|
||||||
|
|
||||||
|
/** Constructor */
|
||||||
|
public ServerPortRange() {
|
||||||
|
super(7000, 7999, 7070, "server");
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/tools/TrackerPortRange.java
Normal file
10
src/tools/TrackerPortRange.java
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
package tools;
|
||||||
|
import tools.PortRange;
|
||||||
|
|
||||||
|
public class TrackerPortRange extends PortRange {
|
||||||
|
|
||||||
|
/** Constructor */
|
||||||
|
public TrackerPortRange() {
|
||||||
|
super(6000, 6999, 6969, "tracker");
|
||||||
|
}
|
||||||
|
}
|
||||||
93
src/tracker/Tracker.java
Normal file
93
src/tracker/Tracker.java
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
201
src/tracker/TrackerManagement.java
Normal file
201
src/tracker/TrackerManagement.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
155
src/tracker/TrackerManagementTCP.java
Normal file
155
src/tracker/TrackerManagementTCP.java
Normal file
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
103
src/tracker/TrackerManagementUDP.java
Normal file
103
src/tracker/TrackerManagementUDP.java
Normal file
@ -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…
Reference in New Issue
Block a user