Merge pull request 'Étape 5' (#84) from etape5 into master
All checks were successful
flavien's git/Projet_JAVA_P2P_STRI2A/pipeline/head This commit looks good

Co-authored-by: js_auge <auge1@hotmail.fr>
Co-authored-by: flavien <flavien.haas@outlook.fr>
This commit is contained in:
Louis Royer 2020-03-30 15:23:10 +02:00
commit dfde8d4527
45 changed files with 1577 additions and 262 deletions

View File

@ -1,18 +1,27 @@
# Projet Répartition : Téléchargement FTP P2P
## Protocol
Documentation of the protocol used can be found in [doc/protocol.md](doc/protocol.md).
## Usage
### Tracker
- Interactive mode: `java tracker.Tracker`
- CLI Arguments mode: `java trackerP2P.trackerP2P -- <hostname> <PORT>` (default port 6969 (range 6000 -> 6999))
### Server-only
- Interactive mode: `java serveurP2P.ServeurP2P`
- CLI Arguments mode: `java serveurP2P.ServeurP2P -- <serveurHOSTNAME> <serveurPORT> <trackerHOSTNAME> <trackerPORT>` (default server port: server 7070 (range 7000->7070) and tracker port 6969 (range 7000 -> 7999))
### Client+Server
- Interactive mode: `java clientP2P.ClientP2P`
- CLI Arguments mode: `java clientP2P.ClientP2P -- <clientTransportProtocol> <integratedServerHOSTNAME> <integratedServerPORT> <trackerHOSTNAME> <trackerPORT>` (default tracker port 6969 (range 7000 -> 7999) and server port: server 7070 (range 7000->7070))
## Sujet
Lien vers le [document original](https://stri-online.net/FTLV/mod/resource/view.php?id=1753).
**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.
# 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.
@ -21,7 +30,7 @@ Plan de travail :
- Concevoir l'application répartie avec UML ;
- Écrire l'application serveur et l'application cliente.
## Étape 2 : Téléchargement en parallèle
### Étape 2 : Téléchargement en parallèle
Dans la seconde étape, on permet à un client de télécharger le fichier depuis plusieurs serveurs.
Le fichier sera découpé en plusieurs blocs de tailles égales (par exemple 4 Ko) qui seront téléchargés depuis plusieurs serveurs.
@ -31,26 +40,26 @@ Plan de travail :
- Modifier le serveur pour gérer l'envoi de n'importe quel bloc d'un fichier ;
- Modifier le client pour qu'il puisse demander le téléchargement de n'importe quel bloc et re-créer le fichier complet.
## Étape 3 : Transformation en P2P simple
### Étape 3 : Transformation en P2P simple
Dans cette étape, il n'y a plus de clients ni de serveurs ; les applications sont les deux à la fois.
Chaque application devra noter de quelle partie du fichier elle dispose. Au démarrage certaines applications auront le fichier complet et les autres aucun bloc.
Les applications demanderont aléatoirement chaque bloc manquant à n'importe quelle autre application qui renverra soit le bloc soit un message d'erreur.
## Étape 4 : P2P coordonné
### Étape 4 : P2P coordonné
Dans cette étape, on ajoute un serveur dont le rôle est de maintenir la liste des applications gérant le téléchargement d'un fichier et quel bloc chaque application possède.
Ce serveur coordonnera le téléchargement en précisant à chaque application, à qui se connecter et ce qui y est disponible.
## Étape 5 : P2P coopératif
### Étape 5 : P2P coopératif
Dans cette étape, on doit s'assurer que les applications envoient et reçoivent globalement les même quantités.
On essaiera ainsi de désavantager les applications qui ne font que télécharger et n'envoient rien.
Options :
- Créer une IHM (interface homme machine) Graphique pour les applications avec Swing par exemple.
- Gérer à la fois des communications UDP et TCP.
- 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).
### Options :
- [ ] Créer une IHM (interface homme machine) Graphique pour les applications avec Swing par exemple.
- [x] Gérer à la fois des communications UDP et TCP.
- [ ] 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).
Note : toute fonctionnalité supplémentaire ne sera prise en compte dans la notation que si toutes les étapes ont été correctement traitées.

View File

@ -1,8 +1,8 @@
# P2P-JAVA-PROJECT version 1.2 (Binary protocol for step 2)
# P2P-JAVA-PROJECT version 1.2 (Binary protocol for step 2+)
All strings in the datagram are utf-8 encoded.
```Datagram format
```text
1 byte: [(byte 0 ): VERSION(0x11, first quartet is major version, second is minor)]
1 byte: [(byte 1 ): REQUEST/RESPONSE CODE]
@ -13,26 +13,33 @@ x bytes: [(bytes 8-?): PAYLOAD]
```
## Requests and responses codes
- REQUESTS (msb is 0):
- `LIST` (0x00)
- `LOAD` (0x01)
- `HASH` (0x02)
- `DISCOVER` (0x03)
- `REGISTER` (0x04)
- `UNREGISTER` (0x05)
- `REQUESTS` (msb is 0):
- `LIST` (`0x00`)
- `LOAD` (`0x01`)
- `HASH` (`0x02`)
- `DISCOVER` (`0x03`)
- `REGISTER` (`0x04`)
- `UNREGISTER` (`0x05`)
- `RATIO` (`0x06`)
- `UPDATE RATIO` (`0x07`)
- `SIZE` (`0x08`)
- RESPONSES (msb is 1):
- `LIST` (0x80)
- `LOAD` (0x81)
- `HASH` (0x82)
- `DISCOVER` (0x83)
- `VERSION ERROR` (0xC0)
- `PROTOCOL ERROR` (0xC1)
- `INTERNAL ERROR` (0xC2)
- `EMPTY DIRECTORY` (0xC3)
- `NOT FOUND` (0xC4)
- `EMPTY FILE` (0xC5)
- `NOT A TRACKER` (0xC6)
- `RESPONSES` (msb is 1):
- `LIST` (`0x80`)
- `LOAD` (`0x81`)
- `HASH` (`0x82`)
- `DISCOVER` (`0x83`)
- `RATIO` (`0x86`)
- `DENIED` (`0x87`)
- `SIZE` (0x88)
- `VERSION ERROR` (`0xC0`)
- `PROTOCOL ERROR` (`0xC1`)
- `INTERNAL ERROR` (`0xC2`)
- `EMPTY DIRECTORY` (`0xC3`)
- `NOT FOUND` (`0xC4`)
- `EMPTY FILE` (`0xC5`)
- `NOT A TRACKER` (`0xC6`)
- `UNKNOWN HOST` (`0xC7`)
### List
Payload size for list request is always zero.
@ -52,9 +59,8 @@ Payload size for Not found is zero.
#### Load response
Payload contains
```
```text
8 bytes: [(bytes 8-15): OFFSET OF FILE CONTENT IN BYTES]
8 bytes: [(bytes 16-23): TOTAL FILESIZE]
4 bytes: [(bytes 24-27): FILENAME SIZE] (cannot be > to PAYLOAD_SIZE - 20 or be zero)
y bytes: [<FILENAME>]
z bytes: [PARTIAL CONTENT]
@ -63,30 +69,50 @@ z bytes: [PARTIAL CONTENT]
#### Load request
Payload contains
```
```text
8 bytes: [(bytes 8-15): OFFSET OF FILE CONTENT IN BYTES]
8 bytes: [(bytes 16-23): MAX SIZE OF PARTIAL CONTENT (partial content in response should not excess this size, but this can be less (by example if endoffile is reached or server doesn't have the full block requested)
4 bytes: [(bytes 24-27): FILENAME SIZE] (cannot be > to PAYLOAD_SIZE - 20 or be zero)
y bytes: [<FILENAME>]
2 bytes: port used to register on tracker
? bytes: hostname used to register on tracker
```
Possible responses: Load response, or Denied
### Size
#### Size request
Payload contains
```text
? bytes: filename
```
Possible responses: Size response, Empty file or Not found
#### Size response
Payload contains
```text
8 bytes: file size
? bytes: filename
```
### Hash
### Hash
#### Hash request
Get hash of a file. Payload contains
```
```text
4 bytes: [(bytes 8-11): FILENAME SIZE]
y bytes: [<FILENAME>]
z bytes: [ALGO_NAMES requested separated by \n] (ex.: SHA-256, MD5)
```
If file does not exists, a NotFound can be responded.
If file does not exists, a Not found can be responded.
#### Hash response
Payload contains:
```
```text
4 bytes: [(bytes 8-11): FILENAME SIZE]
y bytes: [<FILENAME>]
[[ multiple algo hashes bloc]]
@ -94,7 +120,7 @@ y bytes: [<FILENAME>]
A algo hash bloc contains:
```
```text
4 bytes [ALGO_NAME size]
? [ALGO_NAME]
4 bytes: [HASH SIZE (bytes)] / or 0 if this hash algorithm is unsupported.
@ -108,7 +134,7 @@ 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:
```
```text
2 bytes: [<PORT NUMBER>]
```
@ -118,7 +144,7 @@ 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:
```
```text
2 bytes: [<PORT NUMBER>]
? bytes: [<HOSTNAME>]
```
@ -127,14 +153,14 @@ Payload contains:
If payload size is null, lists all servers registered.
If payload contains a filename, list all servers having this file in their list.
```
```text
? bytes: [<FILENAME>]
? bytes: [<HOSTNAME>]
```
#### Discover response
Contains:
```
```text
4 bytes: [(bytes 8-11): FILENAME SIZE]
y bytes: [<FILENAME>]
? bytes [multiple server blocks]
@ -142,16 +168,57 @@ y bytes: [<FILENAME>]
Server block is composed with:
```
```text
2 bytes: [port]
? bytes: hostname
\n
```
#### Not a Tracker
This error is raised when receiving a DISCOVER, a REGISTER, or an UNREGISTER request,
This error is raised when receiving a Discover, a Register, or an Unregister request,
but this application is not a tracker.
#### Ratio Request
Contains:
```text
2 bytes: port
? bytes: hostname
```
Possible responses: Ratio response, or Unknown host
#### Ratio Response
Contains:
```text
8 bytes: total sent bytes
8 bytes: total received bytes
2 bytes: port
? bytes: hostname
```
#### Update Ratio
Contains:
```text
8 bytes: bytes sent by the server
2 bytes: server port
2 bytes: client* port
? bytes: server hostname followed by \n
? bytes: client* hostname
* note: used by client to register on tracker
```
Possible responses: No response, or Unknown host (if client is not registered or server is not registered)
Note: client must have verified hash before sending Update Ratio to tracker for each server which participated.
#### Unknown Host
Payload size is zero.
#### Denied
Contains
```text
8 bytes: offset of content asked
? bytes: filename
```
### Other response code (errors)
#### Version error
Response when datagram received use wrong version code.

View File

@ -1,5 +1,7 @@
package clientP2P;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
@ -24,13 +26,16 @@ import remoteException.ProtocolRemoteError;
import remoteException.NotFound;
import remoteException.InternalRemoteError;
import remoteException.NotATracker;
import remoteException.UnknownHost;
import protocolP2P.HashAlgorithm;
import protocolP2P.HashResponse;
import protocolP2P.HashRequest;
import protocolP2P.Payload;
import protocolP2P.FilePart;
import protocolP2P.LoadRequest;
import protocolP2P.SizeRequest;
import protocolP2P.SizeResponse;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.UpdateRatio;
import clientP2P.ClientDownloadPart;
import tools.HostItem;
import tools.Logger;
@ -48,6 +53,7 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
protected String filename;
protected byte[] hash512;
protected List<ClientDownloadPart> sockList = new ArrayList<ClientDownloadPart>();
protected Map<HostItem, Long> ratioUpdater = new HashMap<>();
protected List<Long> offsetsToAsk = new ArrayList<Long>();
protected List<Long> offsetsPending = new ArrayList<Long>();
protected boolean stop;
@ -57,6 +63,8 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
protected String dirStorage;
protected boolean success = false;
protected Logger logger;
protected HostItem client;
protected HostItem tracker;
/** Constructor with parameters: filename, list of hosts, parts subdirectory and dirStorage
* @param filename name of file to download
@ -64,13 +72,17 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
* @param partsSubdir directory to store .part files
* @param dirStorage directory to write assembled file
* @param logger Logger
* @param client HostItem of the application
* @param tracker HostItem of the tracker
*/
public ClientDownload(String filename, List<HostItem> hostList, String partsSubdir, String dirStorage, Logger logger) {
public ClientDownload(String filename, List<HostItem> hostList, String partsSubdir, String dirStorage, Logger logger, HostItem client, HostItem tracker) {
this.partsSubdir = partsSubdir;
this.dirStorage = dirStorage;
this.filename = filename;
this.hostList = hostList;
this.logger = logger;
this.client = client;
this.tracker = tracker;
this.stop = false;
}
@ -114,8 +126,7 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
try {
sockList.get(rand.nextInt(sockList.size())).assignTask(offset);
offsetsPending.add(offset);
System.err.println("Assigned task "+ offset);
writeLog("Assigned task "+ offset, LogLevel.Info);
writeLog("Assigned task: #"+ offset, LogLevel.Info);
} catch(InterruptedException e) {
writeLog(e, LogLevel.Error);
throw new InternalError();
@ -125,16 +136,15 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
}
/** 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);
protected abstract ClientDownloadPart createDownloadPart(HostItem hostItem);
/** Starts threads for each server in hostList.
*/
protected void initThreads() {
for(HostItem hostItem: hostList) {
sockList.add(createDownloadPart(filename, hostItem));
sockList.add(createDownloadPart(hostItem));
}
for(ClientDownloadPart c: sockList) {
Thread t = new Thread(c);
@ -162,6 +172,7 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
} catch (InterruptedException e) {
throw new InternalError();
}
ratioUpdater.put(c.getServer(), c.getReceivedBytesCount());
}
writeLog("Task check status: " + offsetsToAsk.size() + " to asks, " + offsetsPending.size() + " pending", LogLevel.Info);
if (offsetsToAsk.isEmpty() && offsetsPending.isEmpty()) {
@ -177,6 +188,25 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
}
}
/** Send Ratio update to the tracker
*/
public void sendRatioUpdate() {
for(HostItem server: ratioUpdater.keySet()) {
Long r = ratioUpdater.get(server);
if (r != null) {
long rl = r.longValue();
if (rl != 0) {
try {
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new UpdateRatio(client, server, rl));
d.sendRequest(getHostItemSocket(tracker));
} catch (Exception e) {
writeLog(e, LogLevel.Error);
}
}
}
}
}
/** Get hashsum from server.
* @param hostItem server to ask hash
* @return hash512sum
@ -201,7 +231,6 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
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);
@ -227,6 +256,7 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
*/
protected void purgeList() throws InternalError {
List<HostItem> blackList = new ArrayList<HostItem>();
writeLog("Potential peers (before purge): " + hostList.size(), LogLevel.Debug);
boolean first = false;
byte[] hashsum;
for(HostItem host: hostList) {
@ -245,6 +275,7 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
for(HostItem host: blackList) {
hostList.remove(host);
}
writeLog("Peers (after purge): " + hostList.size(), LogLevel.Debug);
writeLog("Host list purge: done", LogLevel.Info);
}
@ -289,39 +320,22 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
* @throws InternalError
*/
protected void setSize() throws InternalError {
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new LoadRequest(filename, 0, MAX_PARTIAL_SIZE));
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new SizeRequest(filename));
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.");
assert p instanceof SizeResponse : "This payload must be instance of SizeResponse";
if (!(p instanceof SizeResponse)) {
writeLog("cannot get size.", LogLevel.Error);
throw new InternalError();
} else {
FilePart fp = (FilePart)p;
SizeResponse fp = (SizeResponse)p;
if (!fp.getFilename().equals(filename)) {
System.err.println("Error: wrong file received: `" + fp.getFilename() + "`");
writeLog("wrong file received: `" + fp.getFilename() + "`", LogLevel.Error);
writeLog("wrong file size 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();
}
size = fp.getTotalSize();
}
} catch (EmptyDirectory e) {
System.err.println("Error: empty directory.");
@ -354,18 +368,11 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
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();
}
purgeList();
initThreads();
while(!stop) {
assignTasks();
checkTasksStatus();
}
writeLog("Reassembling file parts.", LogLevel.Info);
reassembleFile();
@ -393,7 +400,7 @@ public abstract class ClientDownload extends ServeErrors implements Runnable {
// Add tasks
if (!stop) {
for(long i=MAX_PARTIAL_SIZE; i<size;i+=MAX_PARTIAL_SIZE) {
for(long i=0; i<size;i+=MAX_PARTIAL_SIZE) {
offsetsToAsk.add(Long.valueOf(i));
}
writeLog("Adding tasks: done", LogLevel.Info);

View File

@ -8,6 +8,7 @@ import protocolP2P.ProtocolP2PPacket;
import protocolP2P.Payload;
import protocolP2P.LoadRequest;
import protocolP2P.FilePart;
import protocolP2P.Denied;
import localException.InternalError;
import localException.ProtocolError;
import localException.TransmissionError;
@ -26,6 +27,7 @@ import exception.RemoteException;
import tools.Logger;
import tools.LogLevel;
import tools.ServeErrors;
import tools.HostItem;
/** Class to download file parts.
* @author Louis Royer
@ -34,6 +36,7 @@ import tools.ServeErrors;
* @version 1.0
*/
public abstract class ClientDownloadPart extends ServeErrors implements Runnable {
protected long receivedBytesCount;
protected List<Long> toDoTasks;
protected List<Long> pendingTasks;
protected List<Long> tasksDone;
@ -46,18 +49,24 @@ public abstract class ClientDownloadPart extends ServeErrors implements Runnable
protected static final long MAX_PARTIAL_SIZE = 4096;
protected ClientDownload manager;
protected Logger logger;
protected HostItem client;
private HostItem server;
/** 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
* @param client HostItem of the application
* @param server HostItem of the server application
*/
public ClientDownloadPart(ClientDownload manager, String filename, String partsSubdir, Logger logger) {
public ClientDownloadPart(ClientDownload manager, String filename, String partsSubdir, Logger logger, HostItem client, HostItem server) {
this.manager = manager;
this.partsSubdir = partsSubdir;
this.filename = filename;
this.logger = logger;
this.client = client;
this.server = server;
stop = false;
failed = false;
pendingTasks = new ArrayList<>();
@ -65,6 +74,22 @@ public abstract class ClientDownloadPart extends ServeErrors implements Runnable
tasksDone = new ArrayList<>();
noTask = true;
tasksListsLock = false;
receivedBytesCount = 0;
}
/** receivedBytesCount getter
* @return receivedBytesCount
*/
public Long getReceivedBytesCount() {
return Long.valueOf(receivedBytesCount);
}
/** Server getter
* @return server
*/
public HostItem getServer() {
return server;
}
/** True if thread has failed to get a file.
@ -179,7 +204,6 @@ public abstract class ClientDownloadPart extends ServeErrors implements Runnable
if (p == null) {
stop = true;
}
failed = downloadPart(p);
if (failed) {
System.err.println("Error: DownloadPart failed.");
@ -200,7 +224,7 @@ public abstract class ClientDownloadPart extends ServeErrors implements Runnable
* @return ProtocolP2PPacketTCP used to send request
*/
protected ProtocolP2PPacket<?> reqPart(Long offset) {
writeLog("New request: " + offset, LogLevel.Info);
writeLog("New request: #" + offset, LogLevel.Info);
// maintain tracking of tasks
if (toDoTasks.contains(offset)) {
try {
@ -224,7 +248,7 @@ public abstract class ClientDownloadPart extends ServeErrors implements Runnable
}
// send request
try {
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new LoadRequest(filename, offset.longValue(), MAX_PARTIAL_SIZE));
ProtocolP2PPacket<?> d = createProtocolP2PPacket(new LoadRequest(filename, offset.longValue(), MAX_PARTIAL_SIZE, client));
d.sendRequest(getSocket());
return d;
} catch (InternalError e) {
@ -255,6 +279,22 @@ public abstract class ClientDownloadPart extends ServeErrors implements Runnable
}
try {
Payload p = d.receiveResponse().getPayload();
if (p instanceof Denied) {
Denied denied = (Denied)p;
if (!denied.getFilename().equals(filename)) {
writeLog("wrong file deny response received: `" + denied.getFilename() + "`", LogLevel.Error);
return true;
}
Long offset = Long.valueOf(denied.getOffset());
if (pendingTasks.contains(offset)) {
pendingTasks.remove(offset);
toDoTasks.add(offset);
return false;
} else {
writeLog("wrong file offset deny received: " + offset, LogLevel.Error);
return true;
}
}
assert p instanceof FilePart : "This payload must be instance of FilePart";
if (!(p instanceof FilePart)) {
writeLog("cannot get size.", LogLevel.Error);
@ -267,8 +307,10 @@ public abstract class ClientDownloadPart extends ServeErrors implements Runnable
}
Long offset = Long.valueOf(fp.getOffset());
if (pendingTasks.contains(offset)) {
byte[] partialContent = fp.getPartialContent();
try {
Files.write(new File(partsSubdir + filename + "_" + offset + ".part").toPath(), fp.getPartialContent());
Files.write(new File(partsSubdir + filename + "_" + offset + ".part").toPath(), partialContent);
receivedBytesCount += partialContent.length;
} catch (IOException e) {
writeLog("cannot write file (" + partsSubdir + filename + "_" + offset + ".part)", LogLevel.Error);
}

View File

@ -25,6 +25,7 @@ import remoteException.NotFound;
import remoteException.NotATracker;
import tools.Logger;
import tools.LogLevel;
import tools.HostItem;
import clientP2P.ClientDownloadPart;
/** Class to download file parts on tcp.
@ -42,9 +43,11 @@ public class ClientDownloadPartTCP extends ClientDownloadPart {
* @param socket socket to use
* @param partsSubdir directory to store .part files
* @param logger Logger
* @param client HostItem of the application
* @param server HostItem of the server application
*/
public ClientDownloadPartTCP(ClientDownload manager, String filename, Socket socket, String partsSubdir, Logger logger) {
super(manager, filename, partsSubdir, logger);
public ClientDownloadPartTCP(ClientDownload manager, String filename, Socket socket, String partsSubdir, Logger logger, HostItem client, HostItem server) {
super(manager, filename, partsSubdir, logger, client, server);
this.socket = socket;
}

View File

@ -24,6 +24,7 @@ import java.io.File;
import java.io.IOException;
import tools.Logger;
import tools.LogLevel;
import tools.HostItem;
import clientP2P.ClientDownloadPart;
/** Class to download file parts on udp.
@ -42,9 +43,11 @@ public class ClientDownloadPartUDP extends ClientDownloadPart {
* @param socket socket to use
* @param partsSubdir directory to store .part files
* @param logger Logger
* @param client HostItem of the application
* @param server HostItem of the server application
*/
public ClientDownloadPartUDP(ClientDownload manager, String filename, DatagramSocket socket, String partsSubdir, Logger logger) {
super(manager, filename, partsSubdir, logger);
public ClientDownloadPartUDP(ClientDownload manager, String filename, DatagramSocket socket, String partsSubdir, Logger logger, HostItem client, HostItem server) {
super(manager, filename, partsSubdir, logger, client, server);
this.socket = socket;
}

View File

@ -50,17 +50,18 @@ public class ClientDownloadTCP extends ClientDownload {
* @param partsSubdir directory to store .part files
* @param dirStorage directory to write assembled file
* @param logger Logger
* @param client HostItem of the application
* @param tracker HostItem of the tracker
*/
public ClientDownloadTCP(String filename, List<HostItem> hostList, String partsSubdir, String dirStorage, Logger logger) {
super(filename, hostList, partsSubdir, dirStorage, logger);
public ClientDownloadTCP(String filename, List<HostItem> hostList, String partsSubdir, String dirStorage, Logger logger, HostItem client, HostItem tracker) {
super(filename, hostList, partsSubdir, dirStorage, logger, client, tracker);
}
/** Create a clientDownloadPart
* @param filename name of the file to download
* @param hostItem Hostitem of the server
*/
protected ClientDownloadPart createDownloadPart(String filename, HostItem hostItem) {
return (ClientDownloadPart)new ClientDownloadPartTCP((ClientDownload)this, filename, hostItem.getTCPSocket(), partsSubdir, logger);
protected ClientDownloadPart createDownloadPart(HostItem hostItem) {
return (ClientDownloadPart)new ClientDownloadPartTCP((ClientDownload)this, filename, hostItem.getTCPSocket(), partsSubdir, logger, client, hostItem);
}
/** Close HostItem socket

View File

@ -50,17 +50,18 @@ public class ClientDownloadUDP extends ClientDownload {
* @param partsSubdir directory to store .part files
* @param dirStorage directory to write assembled file
* @param logger Logger
* @param client HostItem of the application
* @param tracker HostItem of the tracker
*/
public ClientDownloadUDP(String filename, List<HostItem> hostList, String partsSubdir, String dirStorage, Logger logger) {
super(filename, hostList, partsSubdir, dirStorage, logger);
public ClientDownloadUDP(String filename, List<HostItem> hostList, String partsSubdir, String dirStorage, Logger logger, HostItem client, HostItem tracker) {
super(filename, hostList, partsSubdir, dirStorage, logger, client, tracker);
}
/** Create a clientDownloadPart
* @param filename name of the file to download
* @param hostItem Hostitem of the server
*/
protected ClientDownloadPart createDownloadPart(String filename, HostItem hostItem) {
return (ClientDownloadPart)new ClientDownloadPartUDP((ClientDownload)this, filename, hostItem.getUDPSocket(), partsSubdir, logger);
protected ClientDownloadPart createDownloadPart(HostItem hostItem) {
return (ClientDownloadPart)new ClientDownloadPartUDP((ClientDownload)this, filename, hostItem.getUDPSocket(), partsSubdir, logger, client, tracker);
}
/** Implementation of writeLog

View File

@ -1,11 +1,21 @@
package clientP2P;
import tools.HostItem;
import tools.Logger;
import tools.LogLevel;
import java.util.Arrays;
import java.util.Scanner;
import java.util.List;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import localException.ProtocolError;
import tools.ServeErrors;
import localException.InternalError;
import localException.ProtocolError;
import localException.SizeError;
import localException.TransmissionError;
import localException.VersionError;
import localException.SocketClosed;
import protocolP2P.RequestResponseCode;
import protocolP2P.FileList;
import protocolP2P.ProtocolP2PPacket;
@ -13,12 +23,6 @@ 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;
@ -26,14 +30,11 @@ 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;
import remoteException.UnknownHost;
import tools.ServeErrors;
import tools.HostItem;
import tools.Logger;
import tools.LogLevel;
/** Implementation of P2P-JAVA-PROJECT CLIENT
* @author Louis Royer
@ -46,23 +47,26 @@ public abstract class ClientManagement extends ServeErrors implements Runnable {
protected String partsSubdir;
protected List<HostItem> hostList;
protected HostItem tracker;
protected HostItem client;
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
* @param client HostItem of the application
*/
public ClientManagement(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, Scanner scanner) {
public ClientManagement(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, Scanner scanner, HostItem client) {
this.scanner = scanner;
this.baseDirectory = baseDirectory;
this.tracker = tracker;
this.partsSubdir = partsSubdir;
this.logger = logger;
this.client = client;
try {
initHostList();
} catch (InternalError e) {
@ -74,7 +78,7 @@ public abstract class ClientManagement extends ServeErrors implements Runnable {
/** Getter for tracker socket
*/
protected abstract Object getTrackerSocket();
protected abstract Object getTrackerSocket();
/** Initialize hostList from tracker
@ -162,6 +166,9 @@ public abstract class ClientManagement extends ServeErrors implements Runnable {
} catch (NotATracker e) {
writeLog(e, LogLevel.Error);
throw new ProtocolError();
} catch (UnknownHost e) {
writeLog(e, LogLevel.Error);
throw new ProtocolError();
}
}
@ -207,6 +214,9 @@ public abstract class ClientManagement extends ServeErrors implements Runnable {
line += "\n";
writeLog(line, LogLevel.Info);
throw new InternalError();
} else {
downLoader.sendRatioUpdate();
writeLog("Ratio updates sent.", LogLevel.Info);
}
} else {
throw new InternalError();
@ -219,44 +229,62 @@ public abstract class ClientManagement extends ServeErrors implements Runnable {
/** 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);
boolean isrunning = true;
while (isrunning){
try {
int i = 1;
String[] list = listDirectory();
System.out.println("Files present on the server:");
System.out.println("0 : Exit the program");
for(String listItem: list) {
System.out.println(i + " : " + listItem);
i++;
}
System.out.println("Type the number associated with the file to download:");
String f = scanner.nextLine();
if(f.equals("0")){
isrunning = false;
}
else{
int j = Integer.parseInt(f);
if(j <= list.length){
j--;
download(list[j]);
System.out.println("File " + f + " sucessfully downloaded");
writeLog("File " + f + " sucessfully downloaded", LogLevel.Info);
}
else{
System.out.println("File " + f + " unsucessfully downloaded, wrong number");
writeLog("File " + f + " unsucessfully downloaded, wrong number", LogLevel.Info);
}
}
} catch (EmptyDirectory e) {
writeLog("Server has no file in directory", LogLevel.Error);
} catch (InternalError e) {
writeLog("Client internal error", LogLevel.Error);
} catch (UnknownHostException e) {
writeLog("Server host is unknown", LogLevel.Error);
} catch (IOException e) {
writeLog("Request cannot be send or response cannot be received", LogLevel.Error);
} catch (TransmissionError e) {
writeLog("Message received is too big", LogLevel.Error);
} catch (ProtocolError e) {
writeLog("Cannot decode servers response", LogLevel.Error);
} catch (VersionError e) {
writeLog("Servers response use bad version of the protocol", LogLevel.Error);
} catch (SizeError e) {
writeLog("Cannot handle this packets because of internal representation limitations of numbers on the client", LogLevel.Error);
} catch (InternalRemoteError e) {
writeLog("Server internal error", LogLevel.Error);
} catch (ProtocolRemoteError e) {
writeLog("Server cannot decode clients request", LogLevel.Error);
} catch (VersionRemoteError e) {
writeLog("Server cannot decode this version of the protocol", LogLevel.Error);
} catch (NotFound e) {
writeLog("Server has not this file in directory", LogLevel.Error);
} catch (EmptyFile e) {
writeLog("File is empty", LogLevel.Error);
}
System.out.println("Name of the file to download:");
String f = scanner.nextLine();
download(f);
System.out.println("File " + f + " sucessfully downloaded");
writeLog("File " + f + " sucessfully downloaded", LogLevel.Info);
} catch (EmptyDirectory e) {
writeLog("Server has no file in directory", LogLevel.Error);
} catch (InternalError e) {
writeLog("Client internal error", LogLevel.Error);
} catch (UnknownHostException e) {
writeLog("Server host is unknown", LogLevel.Error);
} catch (IOException e) {
writeLog("Request cannot be send or response cannot be received", LogLevel.Error);
} catch (TransmissionError e) {
writeLog("Message received is too big", LogLevel.Error);
} catch (ProtocolError e) {
writeLog("Cannot decode servers response", LogLevel.Error);
} catch (VersionError e) {
writeLog("Servers response use bad version of the protocol", LogLevel.Error);
} catch (SizeError e) {
writeLog("Cannot handle this packets because of internal representation limitations of numbers on the client", LogLevel.Error);
} catch (InternalRemoteError e) {
writeLog("Server internal error", LogLevel.Error);
} catch (ProtocolRemoteError e) {
writeLog("Server cannot decode clients request", LogLevel.Error);
} catch (VersionRemoteError e) {
writeLog("Server cannot decode this version of the protocol", LogLevel.Error);
} catch (NotFound e) {
writeLog("Server has not this file in directory", LogLevel.Error);
} catch (EmptyFile e) {
writeLog("File is empty", LogLevel.Error);
}
}
}

View File

@ -24,16 +24,17 @@ public class ClientManagementTCP extends ClientManagement {
* @param partsSubdir subdirectory to store file parts
* @param logger Loggger
* @param scanner Scanner used to read input
* @param client HostItem of the application
*/
public ClientManagementTCP(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, Scanner scanner) {
super(baseDirectory, tracker, partsSubdir, logger, scanner);
public ClientManagementTCP(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, Scanner scanner, HostItem client) {
super(baseDirectory, tracker, partsSubdir, logger, scanner, client);
}
/** Initialize downloader
* @param filename Name of the file to download
*/
protected void initDownloader(String filename) {
downLoader = (ClientDownload) new ClientDownloadTCP(filename, hostList, partsSubdir, baseDirectory, logger);
downLoader = (ClientDownload) new ClientDownloadTCP(filename, hostList, partsSubdir, baseDirectory, logger, client, tracker);
}

View File

@ -23,16 +23,17 @@ public class ClientManagementUDP extends ClientManagement {
* @param partsSubdir subdirectory to store file parts
* @param logger Loggger
* @param scanner Scanner used to read input
* @param client HostItem of the application
*/
public ClientManagementUDP(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, Scanner scanner) {
super(baseDirectory, tracker, partsSubdir, logger, scanner);
public ClientManagementUDP(String baseDirectory, HostItem tracker, String partsSubdir, Logger logger, Scanner scanner, HostItem client) {
super(baseDirectory, tracker, partsSubdir, logger, scanner, client);
}
/** Initialize downloader
* @param filename Name of the file to download
*/
protected void initDownloader(String filename) {
downLoader = (ClientDownload) new ClientDownloadUDP(filename, hostList, partsSubdir, baseDirectory, logger);
downLoader = (ClientDownload) new ClientDownloadUDP(filename, hostList, partsSubdir, baseDirectory, logger, client, tracker);
}
/** Implementation of writeLog

View File

@ -4,7 +4,6 @@ import java.util.Scanner;
import java.util.List;
import clientP2P.ClientManagementUDP;
import clientP2P.ClientManagementTCP;
import serverP2P.ServerManagementUDP;
import serverP2P.ServerManagementTCP;
import tools.Logger;
@ -14,7 +13,6 @@ import tools.HostItem;
import tools.ServerPortRange;
import tools.TrackerPortRange;
/** Client + Server implementation.
* @author Louis Royer
* @author Flavien Haas
@ -31,7 +29,7 @@ public class ClientP2P {
private HostItem tracker;
private HostItem server;
private Scanner scanner;
/** Initialize loggers if directories and logger are null,
* else fail silently.
*/
@ -49,7 +47,7 @@ public class ClientP2P {
* @param hostnameServer hostname to bind
* @param portServer port to bind
* @param hostnameTracker hostname of tracker
* @param portTracker port of tracker
* @param portTracker port of tracker
*/
public ClientP2P(String hostnameServer, int portServer, String hostnameTracker, int portTracker) {
scanner = new Scanner(System.in);
@ -178,7 +176,7 @@ public class ClientP2P {
case "upd": // to avoid users typos
case "2" :
System.out.println("Starting with UDP");
ClientManagementUDP cmudp = new ClientManagementUDP(c.directories.getDataHomeDirectory(), c.tracker, c.directories.getDataHomeDirectory() + c.partsDir, c.loggerClient, c.scanner);
ClientManagementUDP cmudp = new ClientManagementUDP(c.directories.getDataHomeDirectory(), c.tracker, c.directories.getDataHomeDirectory() + c.partsDir, c.loggerClient, c.scanner, c.server);
tclient = new Thread(cmudp);
break;
case "TCP":
@ -186,7 +184,7 @@ public class ClientP2P {
case "1":
default:
System.out.println("Starting with TCP");
ClientManagementTCP cmtcp = new ClientManagementTCP(c.directories.getDataHomeDirectory(), c.tracker, c.directories.getDataHomeDirectory() + c.partsDir, c.loggerClient, c.scanner);
ClientManagementTCP cmtcp = new ClientManagementTCP(c.directories.getDataHomeDirectory(), c.tracker, c.directories.getDataHomeDirectory() + c.partsDir, c.loggerClient, c.scanner, c.server);
tclient = new Thread(cmtcp);
break;
}

View File

@ -0,0 +1,86 @@
package protocolP2P;
import protocolP2P.Payload;
import protocolP2P.RequestResponseCode;
import localException.ProtocolError;
import localException.InternalError;
import localException.SizeError;
import localException.TransmissionError;
import tools.BytesArrayTools;
/** Representation of payload for denied response.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class Denied extends Payload {
private String filename;
private long offset;
static final private int OFFSET_POSITION = PAYLOAD_START_POSITION;
static final private int FILENAME_POSITION = OFFSET_POSITION + 8;
/** Constructor (typically used by server) with informations about file part to send as parameters.
* @param filename name of the file to send
* @param offset where in the file begins the part we are sending
* @throws InternalError
*/
public Denied(String filename, long offset) throws InternalError {
super(RequestResponseCode.DENIED);
/* asserts to help debugging */
assert offset >= 0 : "offset cannot be negative";
assert filename != null : "filename is required";
if (offset < 0 || filename == null) {
throw new InternalError();
}
this.filename = filename;
this.offset = offset;
}
/** Constructor (typically used by client) with Packet received as parameter.
* @param packet the full Packet received
* @throws SizeError
* @throws InternalError
* @throws TransmissionError
*/
protected Denied(byte[] packet) throws TransmissionError, SizeError, ProtocolError, InternalError {
super(packet);
int filenameSize = getPayloadSize(packet) - FILENAME_POSITION + PAYLOAD_START_POSITION;
offset = BytesArrayTools.readLong(packet, OFFSET_POSITION);
filename = BytesArrayTools.readString(packet, FILENAME_POSITION, filenameSize);
}
/** 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 = FILENAME_POSITION + filename.length();
byte[] packet = new byte[size]; // 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 offset to Packet
BytesArrayTools.write(packet, OFFSET_POSITION, offset);
// write filename to Packet
BytesArrayTools.write(packet, filename, FILENAME_POSITION);
return packet;
}
/** filename getter.
* @return String
*/
public String getFilename() {
return filename;
}
/** offset getter.
* @return offset
*/
public long getOffset() {
return offset;
}
}

View File

@ -51,7 +51,7 @@ public class DiscoverRequest extends Payload {
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
byte[] packet = new byte[size]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size

View File

@ -56,7 +56,7 @@ public class DiscoverResponse extends Payload {
int port = BytesArrayTools.readInt16Bits(packet, i);
i += 2;
String hostname = BytesArrayTools.readString(packet, i, "\n");
i += hostname.length();
i += hostname.length() + 1; // 1 for the "\n"
hostList.add(new HostItem(hostname, port));
}
}
@ -75,7 +75,7 @@ public class DiscoverResponse extends Payload {
}
// compute total size
int size = FILENAME_POSITION + filename.length() + hostListSize;
byte[] packet = new byte[size + 1]; // java initialize all to zero
byte[] packet = new byte[size]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size

View File

@ -15,12 +15,10 @@ import tools.BytesArrayTools;
*/
public class FilePart extends Payload {
private String filename;
private long totalSize;
private long offset;
private byte[] partialContent;
static final private int OFFSET_POSITION = PAYLOAD_START_POSITION;
static final private int TOTAL_FILESIZE_POSITION = OFFSET_POSITION + 8;
static final private int FILENAME_SIZE_POSITION = TOTAL_FILESIZE_POSITION + 8;
static final private int FILENAME_SIZE_POSITION = OFFSET_POSITION + 8;
static final private int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
/** Constructor (typically used by server) with informations about file part to send as parameters.
@ -30,20 +28,16 @@ public class FilePart extends Payload {
* @param partialContent content of the file we send
* @throws InternalError
*/
public FilePart(String filename, long totalSize, long offset, byte[] partialContent) throws InternalError {
public FilePart(String filename, long offset, byte[] partialContent) throws InternalError {
super(RequestResponseCode.LOAD_RESPONSE);
/* asserts to help debugging */
assert totalSize >= 0 : "totalSize cannot be negative";
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 offset >= 0 : "offset cannot be negative";
assert filename != null : "filename is required";
if (totalSize < 0 || partialContent.length == 0 || totalSize < partialContent.length
|| offset < 0 || filename == null) {
if (partialContent.length == 0 || offset < 0 || filename == null) {
throw new InternalError();
}
this.filename = filename;
this.totalSize = totalSize;
this.offset = offset;
this.partialContent = partialContent;
}
@ -63,7 +57,6 @@ public class FilePart extends Payload {
throw new InternalError();
}
setOffset(packet); // this can throw SizeError
setTotalSize(packet); // this can throw SizeError
setFilename(packet); // this can throw ProtocolError, SizeError
setPartialContent(packet); // this can throw SizeError
}
@ -77,15 +70,13 @@ public class FilePart extends Payload {
protected byte[] toPacket() throws InternalError {
// compute total size
int size = FILENAME_POSITION + filename.length() + partialContent.length;
byte[] packet = new byte[size + 1]; // java initialize all to zero
byte[] packet = new byte[size]; // 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 offset to Packet
BytesArrayTools.write(packet, OFFSET_POSITION, offset);
// write totalSize to Packet
BytesArrayTools.write(packet, TOTAL_FILESIZE_POSITION, totalSize);
// write filenames size to Packet
BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filename.length());
// write filename to Packet
@ -102,13 +93,6 @@ public class FilePart extends Payload {
private void setOffset(byte[] packet) throws SizeError {
offset = BytesArrayTools.readLong(packet, OFFSET_POSITION);
}
/** Write from Packet into totalSize.
* @param packet received Packet
* @throws SizeError
*/
private void setTotalSize(byte[] packet) throws SizeError {
totalSize = BytesArrayTools.readLong(packet, TOTAL_FILESIZE_POSITION);
}
/** Read filenames size from Packet.
* @param packet received Packet
@ -171,11 +155,4 @@ public class FilePart extends Payload {
public long getOffset() {
return offset;
}
/** totalSize getter.
* @return totalSize
*/
public long getTotalSize() {
return totalSize;
}
}

View File

@ -90,7 +90,7 @@ public class HashRequest extends Payload {
i++;
}
int size = FILENAME_POSITION + filenameSize + BytesArrayTools.computeStringArraySize(algoListStr, "\n");
byte[] packet = new byte[size + 1]; // java initialize all to zero
byte[] packet = new byte[size]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size

View File

@ -98,7 +98,7 @@ public class HashResponse extends Payload {
for (byte[] s : hashes.values()) {
size += 4 + s.length;
}
byte[] packet = new byte[size + 1]; // java initialize all to zero
byte[] packet = new byte[size]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;

View File

@ -6,6 +6,7 @@ import localException.ProtocolError;
import localException.InternalError;
import localException.SizeError;
import tools.BytesArrayTools;
import tools.HostItem;
/** Representation of payload for load request.
* @author Louis Royer
@ -17,18 +18,20 @@ public class LoadRequest extends Payload {
private String filename;
private long maxSizePartialContent;
private long offset;
private HostItem hostItem;
static final private int OFFSET_POSITION = PAYLOAD_START_POSITION;
static final private int MAX_SIZE_PARTIAL_CONTENT_POSITION = OFFSET_POSITION + 8;
static final private int FILENAME_SIZE_POSITION = MAX_SIZE_PARTIAL_CONTENT_POSITION + 8;
static final private int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
/** Constructor (typically used by the server) with a filename parameter.
/** Constructor (typically used by the client) with a filename parameter.
* @param filename name of the file to download. Must not be empty.
* @param offset offset of the bloc
* @param maxSizePartialContent partial content in response should not excess this size, but this can be less (by example if endoffile is reached or server doesn't have the full block requested)
* @param hostItem hostItem used by the client to register on the tracker
* @throws InternalError
*/
public LoadRequest(String filename, long offset, long maxSizePartialContent) throws InternalError {
public LoadRequest(String filename, long offset, long maxSizePartialContent, HostItem hostItem) throws InternalError {
super(RequestResponseCode.LOAD_REQUEST);
/* assert to help debugging */
assert filename.length() != 0 : "Payload size of LoadRequest must not be empty";
@ -38,9 +41,10 @@ public class LoadRequest extends Payload {
this.filename = filename;
this.maxSizePartialContent = maxSizePartialContent;
this.offset = offset;
this.hostItem = hostItem;
}
/** Constructor (typically used by client) with a byte[] parameter containing the Packet received.
/** Constructor (typically used by server) with a byte[] parameter containing the Packet received.
* @param packet the full Packet received
* @throws SizeError
* @throws InternalError
@ -64,6 +68,12 @@ public class LoadRequest extends Payload {
/* Read filename */
int size = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION);
filename = BytesArrayTools.readString(packet, FILENAME_POSITION, size);
/* Read hostItem */
int portPosition = FILENAME_POSITION + size;
int hostnameStartPosition = portPosition + 2;
int hostnameSize = getPayloadSize(packet) - hostnameStartPosition + PAYLOAD_START_POSITION;
hostItem = new HostItem(BytesArrayTools.readString(packet, hostnameStartPosition, hostnameSize), BytesArrayTools.readInt16Bits(packet, portPosition));
}
/** Returns a byte[] containing Packet with padding.
@ -75,8 +85,9 @@ public class LoadRequest extends Payload {
protected byte[] toPacket() throws InternalError {
// compute size
int filenameSize = filename.length();
int size = FILENAME_POSITION + filenameSize;
byte[] packet = new byte[size + 1]; // java initialize all to zero
String hostname = hostItem.getHostname();
int size = FILENAME_POSITION + filenameSize + 2 + hostname.length();
byte[] packet = new byte[size]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size
@ -89,7 +100,15 @@ public class LoadRequest extends Payload {
BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filenameSize);
// Write filename
BytesArrayTools.write(packet, filename, FILENAME_POSITION);
return packet;
// Write hostitem
int portPosition = FILENAME_POSITION + filenameSize;
try {
BytesArrayTools.write16Bits(packet, portPosition, hostItem.getPort());
BytesArrayTools.write(packet, hostname, portPosition + 2);
return packet;
} catch (SizeError e) {
throw new InternalError();
}
}
/** filename getter.
@ -112,4 +131,11 @@ public class LoadRequest extends Payload {
public long getMaxSizePartialContent() {
return maxSizePartialContent;
}
/** hostItem getter.
* @return hostItem
*/
public HostItem getHostItem() {
return hostItem;
}
}

View File

@ -9,6 +9,12 @@ import protocolP2P.DiscoverRequest;
import protocolP2P.DiscoverResponse;
import protocolP2P.Register;
import protocolP2P.Unregister;
import protocolP2P.RatioRequest;
import protocolP2P.RatioResponse;
import protocolP2P.UpdateRatio;
import protocolP2P.Denied;
import protocolP2P.SizeRequest;
import protocolP2P.SizeResponse;
import localException.ProtocolError;
import localException.InternalError;
import localException.TransmissionError;
@ -40,6 +46,12 @@ public class Payload {
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";
assert requestResponseCode != RequestResponseCode.RATIO_REQUEST || (this instanceof RatioRequest) : "RATIO_REQUEST must use RatioRequest class";
assert requestResponseCode != RequestResponseCode.RATIO_RESPONSE || (this instanceof RatioResponse) : "RATIO_RESPONSE must use RatioResponse class";
assert requestResponseCode != RequestResponseCode.UPDATE_RATIO || (this instanceof UpdateRatio) : "UPDATE_RATIO must use UpdateRatio class";
assert requestResponseCode != RequestResponseCode.DENIED || (this instanceof Denied) : "DENIED must use Denied class";
assert requestResponseCode != RequestResponseCode.SIZE_REQUEST || (this instanceof SizeRequest) : "SIZE_REQUEST must use SizeRequest class";
assert requestResponseCode != RequestResponseCode.SIZE_RESPONSE || (this instanceof SizeResponse) : "SIZE_RESPONSE must use SizeResponse class";
this.requestResponseCode = requestResponseCode;
checkRequestResponseCode(); // this can throw InternalError
}
@ -67,6 +79,12 @@ public class Payload {
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";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.RATIO_REQUEST || (this instanceof RatioRequest) : "RATIO_REQUEST must use RatioRequest class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.RATIO_RESPONSE || (this instanceof RatioResponse) : "RATIO_RESPONSE must use RatioResponse class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.UPDATE_RATIO || (this instanceof UpdateRatio) : "UPDATE_RATIO must use UpdateRatio class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.DENIED || (this instanceof Denied) : "DENIED must use Denied class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.SIZE_REQUEST || (this instanceof SizeRequest) : "SIZE_REQUEST must use SizeRequest class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.SIZE_RESPONSE || (this instanceof SizeResponse) : "SIZE_RESPONSE must use SizeResponse class";
requestResponseCode = RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]);
checkRequestResponseCode(); // this can throw InternalError
}
@ -85,6 +103,12 @@ public class Payload {
|| (requestResponseCode == RequestResponseCode.DISCOVER_RESPONSE && !(this instanceof DiscoverResponse))
|| (requestResponseCode == RequestResponseCode.REGISTER && !(this instanceof Register))
|| (requestResponseCode == RequestResponseCode.UNREGISTER && !(this instanceof Unregister))
|| (requestResponseCode == RequestResponseCode.RATIO_REQUEST && !(this instanceof RatioRequest))
|| (requestResponseCode == RequestResponseCode.RATIO_RESPONSE && !(this instanceof RatioResponse))
|| (requestResponseCode == RequestResponseCode.UPDATE_RATIO && !(this instanceof UpdateRatio))
|| (requestResponseCode == RequestResponseCode.DENIED && !(this instanceof Denied))
|| (requestResponseCode == RequestResponseCode.SIZE_REQUEST && !(this instanceof SizeRequest))
|| (requestResponseCode == RequestResponseCode.SIZE_RESPONSE && !(this instanceof SizeResponse))
) {
throw new InternalError();
}

View File

@ -12,6 +12,7 @@ import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.EmptyFile;
import remoteException.NotATracker;
import remoteException.UnknownHost;
import java.io.IOException;
import tools.HostItem;
@ -72,8 +73,9 @@ public abstract class ProtocolP2PPacket < T extends Payload>{
* @throws SizeError
* @throws IOException
* @throws SocketClosed
* @throws UnknownHost
*/
public abstract ProtocolP2PPacket<?> receiveResponse() throws EmptyFile, NotFound, NotATracker, 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, UnknownHost;
/** Receive a request, subclasses must overwrite this constructor.
* @param socket socket used to get the request

View File

@ -12,12 +12,19 @@ import remoteException.NotFound;
import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.EmptyFile;
import remoteException.UnknownHost;
import tools.HostItem;
import protocolP2P.Payload;
import protocolP2P.RequestResponseCode;
import protocolP2P.LoadRequest;
import protocolP2P.FileList;
import protocolP2P.FilePart;
import protocolP2P.RatioRequest;
import protocolP2P.RatioResponse;
import protocolP2P.UpdateRatio;
import protocolP2P.Denied;
import protocolP2P.SizeRequest;
import protocolP2P.SizeResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
@ -141,6 +148,9 @@ public class ProtocolP2PPacketTCP < T extends Payload > extends ProtocolP2PPacke
case HASH_RESPONSE:
case DISCOVER_RESPONSE:
case NOT_A_TRACKER:
case RATIO_RESPONSE:
case DENIED:
case SIZE_RESPONSE:
// we were expecting a request, but we are receiving a response
throw new ProtocolError();
default :
@ -203,8 +213,9 @@ public class ProtocolP2PPacketTCP < T extends Payload > extends ProtocolP2PPacke
* @throws SizeError
* @throws IOException
* @throws SocketClosed
* @throws UnknownHost
*/
public ProtocolP2PPacket<?> receiveResponse() throws EmptyFile, NotFound, NotATracker, 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, UnknownHost {
assert requestSocket != null : "Cannot receive response because request packet not sent.";
if (requestSocket == null) {
throw new InternalError();
@ -244,6 +255,8 @@ public class ProtocolP2PPacketTCP < T extends Payload > extends ProtocolP2PPacke
throw new EmptyFile();
case NOT_A_TRACKER:
throw new NotATracker();
case UNKNOWN_HOST:
throw new UnknownHost();
default :
return (ProtocolP2PPacket)p;
}
@ -319,6 +332,24 @@ public class ProtocolP2PPacketTCP < T extends Payload > extends ProtocolP2PPacke
case DISCOVER_RESPONSE:
payload = (Payload) new DiscoverResponse(packet);
break;
case RATIO_REQUEST:
payload = (Payload) new RatioRequest(packet);
break;
case RATIO_RESPONSE:
payload = (Payload) new RatioResponse(packet);
break;
case UPDATE_RATIO:
payload = (Payload) new UpdateRatio(packet);
break;
case DENIED:
payload = (Payload) new Denied(packet);
break;
case SIZE_REQUEST:
payload = (Payload) new SizeRequest(packet);
break;
case SIZE_RESPONSE:
payload = (Payload) new SizeResponse(packet);
break;
default:
payload = new Payload(packet); // this can throw TransmissionError
break;

View File

@ -12,6 +12,7 @@ import remoteException.NotFound;
import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.EmptyFile;
import remoteException.UnknownHost;
import tools.BytesArrayTools;
import tools.HostItem;
import protocolP2P.Payload;
@ -19,6 +20,12 @@ import protocolP2P.RequestResponseCode;
import protocolP2P.LoadRequest;
import protocolP2P.FileList;
import protocolP2P.FilePart;
import protocolP2P.RatioRequest;
import protocolP2P.RatioResponse;
import protocolP2P.UpdateRatio;
import protocolP2P.Denied;
import protocolP2P.SizeRequest;
import protocolP2P.SizeResponse;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
@ -134,6 +141,9 @@ public class ProtocolP2PPacketUDP < T extends Payload> extends ProtocolP2PPacket
case HASH_RESPONSE:
case DISCOVER_RESPONSE:
case NOT_A_TRACKER:
case RATIO_RESPONSE:
case DENIED:
case SIZE_RESPONSE:
// we were expecting a request, but we are receiving a response
throw new ProtocolError();
default :
@ -198,8 +208,9 @@ public class ProtocolP2PPacketUDP < T extends Payload> extends ProtocolP2PPacket
* @throws InternalError
* @throws SizeError
* @throws IOException
* @throws UnknownHost
*/
public ProtocolP2PPacket<?> receiveResponse() throws EmptyFile, NotFound, NotATracker, 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, UnknownHost {
assert requestSocket != null : "Cannot receive response because request packet not sent.";
if (requestSocket == null) {
throw new InternalError();
@ -228,6 +239,8 @@ public class ProtocolP2PPacketUDP < T extends Payload> extends ProtocolP2PPacket
throw new EmptyFile();
case NOT_A_TRACKER:
throw new NotATracker();
case UNKNOWN_HOST:
throw new UnknownHost();
default :
return (ProtocolP2PPacket)p;
}
@ -304,6 +317,24 @@ public class ProtocolP2PPacketUDP < T extends Payload> extends ProtocolP2PPacket
case DISCOVER_RESPONSE:
payload = (Payload) new DiscoverResponse(packet);
break;
case RATIO_REQUEST:
payload = (Payload) new RatioRequest(packet);
break;
case RATIO_RESPONSE:
payload = (Payload) new RatioResponse(packet);
break;
case UPDATE_RATIO:
payload = (Payload) new UpdateRatio(packet);
break;
case DENIED:
payload = (Payload) new Denied(packet);
break;
case SIZE_REQUEST:
payload = (Payload) new SizeRequest(packet);
break;
case SIZE_RESPONSE:
payload = (Payload) new SizeResponse(packet);
break;
default:
payload = new Payload(packet); // this can throw TransmissionError
break;

View 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 ratio request.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class RatioRequest extends Payload {
private HostItem hostItem;
private static final int HOSTNAME_START_POSITION = PAYLOAD_START_POSITION + 2;
/** Constructor with hostItem (typically used by client/server)
* @param hostItem Host to get ratio of.
* @throws InternalError
*/
public RatioRequest(HostItem hostItem) throws InternalError {
super(RequestResponseCode.RATIO_REQUEST);
this.hostItem = hostItem;
}
/** Constructor (typically used by tracker) with a byte[] parameter containing the Packet received.
* @param packet the full Packet received
* @throws SizeError
* @throws InternalError
* @throws ProtocolError
* @throws TransmissionError
*/
protected RatioRequest(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]; // 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;
}
}

View File

@ -0,0 +1,106 @@
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 ratio response.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class RatioResponse extends Payload {
private HostItem hostItem;
private long totalUp;
private long totalDown;
private static final int TOTAL_UP_START_POSITION = PAYLOAD_START_POSITION;
private static final int TOTAL_DOWN_START_POSITION = TOTAL_UP_START_POSITION + 8;
private static final int PORT_START_POSITION = TOTAL_DOWN_START_POSITION + 8;
private static final int HOSTNAME_START_POSITION = PORT_START_POSITION + 2;
/** Constructor with hostItem (typically used by tracker)
* @param hostItem Host to get ratio of.
* @param totalUp total bytes uploaded
* @param totalDown total bytes downloaded
* @throws InternalError
*/
public RatioResponse(HostItem hostItem, long totalUp, long totalDown) throws InternalError {
super(RequestResponseCode.RATIO_RESPONSE);
this.hostItem = hostItem;
this.totalUp = totalUp;
this.totalDown = totalDown;
}
/** Constructor (typically used by client/server) with a byte[] parameter containing the Packet received.
* @param packet the full Packet received
* @throws SizeError
* @throws InternalError
* @throws ProtocolError
* @throws TransmissionError
*/
protected RatioResponse(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
super(packet);
int size = getPayloadSize(packet);
totalUp = BytesArrayTools.readLong(packet, TOTAL_UP_START_POSITION);
totalDown = BytesArrayTools.readLong(packet, TOTAL_DOWN_START_POSITION);
int port = BytesArrayTools.readInt16Bits(packet, PORT_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]; // 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 totalUp
BytesArrayTools.write(packet, TOTAL_UP_START_POSITION, totalUp);
// write totalDown
BytesArrayTools.write(packet, TOTAL_DOWN_START_POSITION, totalDown);
// write port to Packet
try {
BytesArrayTools.write16Bits(packet, PORT_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;
}
/** totalUp getter.
* @return totalUp
*/
public long getTotalUp() {
return totalUp;
}
/** totalDown getter.
* @return totalDown
*/
public long getTotalDown() {
return totalDown;
}
}

View File

@ -51,7 +51,7 @@ public class Register extends Payload {
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
byte[] packet = new byte[size]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size

View File

@ -18,17 +18,24 @@ public enum RequestResponseCode {
DISCOVER_REQUEST(CodeType.REQUEST_TRACKER, (byte)0x03),
REGISTER(CodeType.REQUEST_TRACKER, (byte)0x04),
UNREGISTER(CodeType.REQUEST_TRACKER, (byte)0x05),
RATIO_REQUEST(CodeType.REQUEST_TRACKER, (byte)0x06),
UPDATE_RATIO(CodeType.REQUEST_TRACKER, (byte)0x07),
SIZE_REQUEST(CodeType.REQUEST, (byte)0x08),
LIST_RESPONSE(CodeType.RESPONSE, (byte)0x80),
LOAD_RESPONSE(CodeType.RESPONSE, (byte)0x81),
HASH_RESPONSE(CodeType.RESPONSE, (byte)0x82),
DISCOVER_RESPONSE(CodeType.RESPONSE, (byte)0x83),
RATIO_RESPONSE(CodeType.RESPONSE, (byte)0x86),
DENIED(CodeType.RESPONSE, (byte)0x87),
SIZE_RESPONSE(CodeType.RESPONSE, (byte)0x88),
VERSION_ERROR(CodeType.ERROR, (byte)0xC0),
PROTOCOL_ERROR(CodeType.ERROR, (byte)0xC1),
INTERNAL_ERROR(CodeType.ERROR, (byte)0xC2),
EMPTY_DIRECTORY(CodeType.ERROR, (byte)0xC3),
NOT_FOUND(CodeType.ERROR, (byte)0xC4),
EMPTY_FILE(CodeType.ERROR, (byte)0xC5),
NOT_A_TRACKER(CodeType.ERROR, (byte)0xC6);
NOT_A_TRACKER(CodeType.ERROR, (byte)0xC6),
UNKNOWN_HOST(CodeType.ERROR, (byte)0xC7);
public final CodeType codeType;
public final byte codeValue;

View File

@ -0,0 +1,70 @@
package protocolP2P;
import protocolP2P.Payload;
import protocolP2P.RequestResponseCode;
import localException.TransmissionError;
import localException.ProtocolError;
import localException.InternalError;
import localException.SizeError;
import tools.BytesArrayTools;
import tools.HostItem;
/** Representation of payload for load request.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class SizeRequest extends Payload {
private String filename;
/** Constructor (typically used by the client) with a filename parameter.
* @param filename name of the file to download. Must not be empty.
*/
public SizeRequest(String filename) throws InternalError {
super(RequestResponseCode.SIZE_REQUEST);
/* assert to help debugging */
assert filename.length() != 0 : "Payload size of LoadRequest must not be empty";
if (filename.length() == 0) {
throw new InternalError();
}
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 SizeRequest(byte[] packet) throws TransmissionError, SizeError, ProtocolError, InternalError {
super(packet);
/* Read filename */
int size = getPayloadSize(packet);
filename = BytesArrayTools.readString(packet, 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 {
int size = PAYLOAD_START_POSITION + filename.length();
byte[] packet = new byte[size]; // 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
BytesArrayTools.write(packet, filename, PAYLOAD_START_POSITION);
return packet;
}
/** filename getter.
* @return filename
*/
public String getFilename() {
return filename;
}
}

View File

@ -0,0 +1,86 @@
package protocolP2P;
import protocolP2P.Payload;
import protocolP2P.RequestResponseCode;
import localException.ProtocolError;
import localException.InternalError;
import localException.SizeError;
import localException.TransmissionError;
import tools.BytesArrayTools;
/** Representation of payload for size response.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class SizeResponse extends Payload {
private String filename;
private long totalSize;
static final private int TOTAL_SIZE_POSITION = PAYLOAD_START_POSITION;
static final private int FILENAME_POSITION = TOTAL_SIZE_POSITION + 8;
/** Constructor (typically used by server) with informations about file part to send as parameters.
* @param filename name of the file
* @param totalSize size of the file
* @throws InternalError
*/
public SizeResponse(String filename, long totalSize) throws InternalError {
super(RequestResponseCode.SIZE_RESPONSE);
/* asserts to help debugging */
assert totalSize >= 0 : "offset cannot be negative";
assert filename != null : "filename is required";
if (filename == null || totalSize < 0) {
throw new InternalError();
}
this.filename = filename;
this.totalSize = totalSize;
}
/** Constructor (typically used by client) with Packet received as parameter.
* @param packet the full Packet received
* @throws SizeError
* @throws InternalError
* @throws TransmissionError
*/
protected SizeResponse(byte[] packet) throws TransmissionError, SizeError, ProtocolError, InternalError {
super(packet);
int filenameSize = getPayloadSize(packet) - FILENAME_POSITION + PAYLOAD_START_POSITION;
totalSize = BytesArrayTools.readLong(packet, TOTAL_SIZE_POSITION);
filename = BytesArrayTools.readString(packet, FILENAME_POSITION, filenameSize);
}
/** 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 = FILENAME_POSITION + filename.length();
byte[] packet = new byte[size]; // 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 totalSize to Packet
BytesArrayTools.write(packet, TOTAL_SIZE_POSITION, totalSize);
// write filename to Packet
BytesArrayTools.write(packet, filename, FILENAME_POSITION);
return packet;
}
/** filename getter.
* @return String
*/
public String getFilename() {
return filename;
}
/** totalSize getter.
* @return totalSize
*/
public long getTotalSize() {
return totalSize;
}
}

View File

@ -51,7 +51,7 @@ public class Unregister extends Payload {
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
byte[] packet = new byte[size]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size

View File

@ -0,0 +1,112 @@
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 update ratio.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class UpdateRatio extends Payload {
private HostItem client;
private HostItem server;
private long dataSize;
private static final int DATA_SIZE_POSITION = PAYLOAD_START_POSITION;
private static final int SERVER_PORT_START_POSITION = DATA_SIZE_POSITION + 8;
private static final int CLIENT_PORT_START_POSITION = SERVER_PORT_START_POSITION + 2;
private static final int HOSTNAMES_START_POSITION = CLIENT_PORT_START_POSITION + 2;
/** Constructor with hostItem (typically used by client)
* @param client HostItem of the client application.
* @param server HostItem of the server application.
* @param dataSize size of data sent.
* @throws InternalError
*/
public UpdateRatio(HostItem client, HostItem server, long dataSize) throws InternalError {
super(RequestResponseCode.UPDATE_RATIO);
this.client = client;
this.server = server;
this.dataSize = dataSize;
}
/** Constructor (typically used by tracker) with a byte[] parameter containing the Packet received.
* @param packet the full Packet received
* @throws SizeError
* @throws InternalError
* @throws ProtocolError
* @throws TransmissionError
*/
protected UpdateRatio(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
super(packet);
int size = getPayloadSize(packet);
dataSize = BytesArrayTools.readLong(packet, DATA_SIZE_POSITION);
int portServer = BytesArrayTools.readInt16Bits(packet, SERVER_PORT_START_POSITION);
int portClient = BytesArrayTools.readInt16Bits(packet, CLIENT_PORT_START_POSITION);
String[] hostnames = BytesArrayTools.readStringArray(packet, HOSTNAMES_START_POSITION, size - HOSTNAMES_START_POSITION + PAYLOAD_START_POSITION, "\n");
server = new HostItem(hostnames[0], portServer);
client = new HostItem(hostnames[1], portClient);
}
/** 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[] hostnames = { server.getHostname(), client.getHostname()};
// compute total size
int size = HOSTNAMES_START_POSITION + BytesArrayTools.computeStringArraySize(hostnames, "\n");
byte[] packet = new byte[size]; // 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 dataSize
BytesArrayTools.write(packet, DATA_SIZE_POSITION, dataSize);
// write server port to Packet
try {
BytesArrayTools.write16Bits(packet, SERVER_PORT_START_POSITION, server.getPort());
} catch (SizeError e) {
throw new InternalError();
}
// write client port to Packet
try {
BytesArrayTools.write16Bits(packet, CLIENT_PORT_START_POSITION, client.getPort());
} catch (SizeError e) {
throw new InternalError();
}
// write hostnames to Packet
BytesArrayTools.write(packet, hostnames, HOSTNAMES_START_POSITION, "\n");
return packet;
}
/** Client getter.
* @return client
*/
public HostItem getClient() {
return client;
}
/** Server getter
* @return server
*/
public HostItem getServer() {
return server;
}
/** dataSize getter.
* @return dataSize
*/
public long getDataSize() {
return dataSize;
}
}

View File

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

View File

@ -30,6 +30,7 @@ public abstract class FileWatcher implements Runnable {
protected HostItem tracker;
protected String baseDirectory;
protected Map<String, byte[]> sha512 = new HashMap<>();
protected Thread thread;
/** Constructor
@ -83,7 +84,7 @@ public abstract class FileWatcher implements Runnable {
try {
Thread.sleep(time);
} catch(InterruptedException e) {
writeLog("File list watcher interrupted", LogLevel.Error);
writeLog("File list watcher interrupted", LogLevel.Info);
setStop();
}
}
@ -123,6 +124,9 @@ public abstract class FileWatcher implements Runnable {
*/
public void setStop() {
stop = true;
if (thread != null) {
thread.interrupt();
}
}
/** Init sha512 map.
@ -153,4 +157,11 @@ public abstract class FileWatcher implements Runnable {
*/
protected abstract void writeLog(Exception e, LogLevel logLevel);
/** Set thread
* @param thread Thread
*/
public void setThread(Thread thread) {
this.thread = thread;
}
}

View File

@ -0,0 +1,177 @@
package serverP2P;
import tools.Logger;
import tools.LogLevel;
import tools.HostItem;
import java.util.Map;
import java.util.HashMap;
import java.io.IOException;
import exception.RemoteException;
import exception.LocalException;
import protocolP2P.RatioRequest;
import protocolP2P.RatioResponse;
import protocolP2P.Payload;
import protocolP2P.ProtocolP2PPacket;
import remoteException.UnknownHost;
import remoteException.NotATracker;
import localException.InternalError;
/** Class allowing to keep the tracker informed about ratios
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public abstract class RatioWatcher implements Runnable {
final static double punishmentFactor = 1.2;
protected Logger logger;
protected volatile boolean stop;
protected long time;
protected boolean force;
protected HostItem tracker;
protected Thread thread;
protected Map<HostItem, Double> cachePunishmentProbability = new HashMap<>();
protected boolean lock;
/** Constructor
* @param logger Logger
* @param millis Time interval before recheck
* @param tracker HostItem for the tracker
*/
public RatioWatcher(Logger logger, long millis, HostItem tracker) {
assert logger != null : "Logger is null";
assert tracker != null : "Tracker is null";
this.logger = logger;
time = millis;
this.tracker = tracker;
lock = false;
}
/** Runnable implementation */
public void run() {
writeLog("Ratio watcher started : delay " + time + " milliseconds.", LogLevel.Info);
while(!stop) {
try {
clean();
Thread.sleep(time);
} catch(InterruptedException e) {
writeLog("Ratio watcher interrupted", LogLevel.Info);
setStop();
}
}
}
/** Invalidate the cache by cleaning all hashmaps
* @throws InterruptedException
*/
protected synchronized void clean() throws InterruptedException{
while(lock) {
this.wait();
}
lock = true;
cachePunishmentProbability.clear();
lock = false;
this.notifyAll();
}
/** Get Up-ratio for an applications
* @param application HostItem of the application
* @return Punishment Probability
* @throws UnknownHost
*/
protected synchronized double getPunishmentProbability(HostItem application) throws InternalError, UnknownHost {
try {
while(lock) {
this.wait();
}
lock = true;
if (!cachePunishmentProbability.containsKey(application)) {
// update if not in cache
try {
ProtocolP2PPacket<?> p = createProtocolP2PPacket(new RatioRequest(application));
p.sendRequest(getTrackerSocket());
Payload resp = p.receiveResponse().getPayload();
if (!(resp instanceof RatioResponse)) {
throw new InternalError();
}
RatioResponse rresp = (RatioResponse)resp;
if (!rresp.getHostItem().equals(application)) {
writeLog("Ratio response host is not the expected one. Expected : "
+ application + ". Received : " + rresp.getHostItem(), LogLevel.Debug);
throw new InternalError();
}
long up = rresp.getTotalUp();
long down = rresp.getTotalDown();
assert punishmentFactor > 1 : "The punishment factor must be greater than 1";
if (down == 0 || (punishmentFactor * up) >= down) {
cachePunishmentProbability.put(application, Double.valueOf(0));
} else {
cachePunishmentProbability.put(application, Double.valueOf((down - up)/(down * punishmentFactor)));
}
} catch (UnknownHost e) {
throw e;
} catch (IOException e) {
throw new InternalError();
} catch (LocalException e) {
writeLog(e, LogLevel.Error);
throw new InternalError();
} catch (RemoteException e) {
writeLog(e, LogLevel.Error);
throw new InternalError();
}
}
double ret = cachePunishmentProbability.get(application);
lock = false;
this.notifyAll();
return ret;
} catch (InterruptedException e) {
throw new InternalError();
}
}
/** Ask the thread to stop
*/
public void setStop() {
stop = true;
if (thread != null) {
thread.interrupt();
}
}
/** 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);
/** Set thread
* @param thread Thread
*/
public void setThread(Thread thread) {
this.thread = thread;
}
/** Create packets
* @param payload Payload
*/
protected abstract < T extends Payload > ProtocolP2PPacket<?> createProtocolP2PPacket(T payload);
/** Tracker socket getter
* @return tracker socket
*/
protected abstract Object getTrackerSocket();
/** Closes tracker socket
*/
protected abstract void closeTrackerSocket();
}

View File

@ -0,0 +1,66 @@
package serverP2P;
import tools.Logger;
import tools.LogLevel;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.ProtocolP2PPacketTCP;
import protocolP2P.Payload;
import tools.HostItem;
import serverP2P.RatioWatcher;
/** 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 RatioWatcherTCP extends RatioWatcher {
/** Constructor
* @param logger Logger
* @param millis Time interval before recheck
* @param tracker HostItem for the tracker
*/
public RatioWatcherTCP(Logger logger, long millis, HostItem tracker) {
super(logger, millis, tracker);
assert logger != null : "Logger is null";
assert tracker != null : "Tracker 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);
}
/** Create packets
* @param payload Payload
*/
protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketTCP<T>(payload);
}
/** Tracker socket getter
* @return tracker socket
*/
protected Object getTrackerSocket() {
return tracker.getTCPSocket();
}
/** Closes tracker socket
*/
protected void closeTrackerSocket() {
tracker.closeTCPSocket();
}
}

View File

@ -0,0 +1,64 @@
package serverP2P;
import tools.Logger;
import tools.LogLevel;
import protocolP2P.ProtocolP2PPacket;
import protocolP2P.ProtocolP2PPacketUDP;
import protocolP2P.Payload;
import tools.HostItem;
import serverP2P.RatioWatcher;
/** 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 RatioWatcherUDP extends RatioWatcher {
/** Constructor
* @param logger Logger
* @param millis Time interval before recheck
* @param tracker HostItem for the tracker
*/
public RatioWatcherUDP(Logger logger, long millis, HostItem tracker) {
super(logger, millis, tracker);
assert logger != null : "Logger is null";
assert tracker != null : "Tracker 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);
}
/** Create packets
* @param payload Payload
*/
protected < T extends Payload > ProtocolP2PPacket<T> createProtocolP2PPacket(T payload) {
return (ProtocolP2PPacket<T>)new ProtocolP2PPacketUDP<T>(payload);
}
/** Tracker socket getter
* @return tracker socket
*/
protected Object getTrackerSocket() {
return tracker.getUDPSocket();
}
/** Closes tracker socket
*/
protected void closeTrackerSocket() {
tracker.closeUDPSocket();
}
}

View File

@ -1,5 +1,6 @@
package serverP2P;
import serverP2P.FileWatcher;
import serverP2P.RatioWatcher;
import tools.Logger;
import tools.LogLevel;
import tools.HostItem;
@ -14,13 +15,20 @@ import protocolP2P.HashRequest;
import protocolP2P.HashResponse;
import protocolP2P.HashAlgorithm;
import protocolP2P.Unregister;
import protocolP2P.SizeRequest;
import protocolP2P.SizeResponse;
import protocolP2P.Denied;
import java.nio.file.Paths;
import java.nio.file.Files;
import java.io.File;
import java.util.Arrays;
import java.util.Map;
import java.util.HashMap;
import java.util.Random;
import java.io.IOException;
import exception.LocalException;
import localException.InternalError;
import remoteException.UnknownHost;
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol.
* @author Louis Royer
@ -36,6 +44,8 @@ public abstract class ServerManagement extends ServeErrors implements Runnable {
protected String baseDirectory;
protected HostItem server;
protected HostItem tracker;
protected Random punisher = new Random();
protected RatioWatcher ratioWatcher;
/** Constructor */
public ServerManagement(String baseDirectory, HostItem server, HostItem tracker, Logger logger) {
@ -54,8 +64,15 @@ public abstract class ServerManagement extends ServeErrors implements Runnable {
/** Stop the thread */
public void setStop() {
stop = true;
fileListWatcher.setStop();
ratioWatcher.setStop();
sendUnregisterRequest();
closeSocket();
}
/** Closes socket */
protected abstract void closeSocket();
/** Trigger a manual check of the file list
*/
public void updateFileList() {
@ -136,27 +153,48 @@ public abstract class ServerManagement extends ServeErrors implements Runnable {
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);
double proba = ratioWatcher.getPunishmentProbability(((LoadRequest)p).getHostItem());
if (punisher.nextDouble() <= proba) {
writeLog("Sending punishment", LogLevel.Debug);
pd.sendResponse(createProtocolP2PPacket(new Denied(filename, offset)));
} else {
pd.sendResponse(createProtocolP2PPacket((Payload)(new FilePart(filename, fullLoad.length, offset, load))));
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));
try {
if (load.length == 0) {
sendEmptyFile(pd);
} else {
pd.sendResponse(createProtocolP2PPacket((Payload)(new FilePart(filename, offset, load))));
}
} catch (Exception e2) {
writeLog(e2, LogLevel.Error);
}
}
} catch (Exception e2) {
writeLog(e2, LogLevel.Error);
} catch (InternalError e) {
writeLog("InternalError", LogLevel.Debug);
writeLog(e, LogLevel.Debug);
sendInternalError(pd);
return;
} catch (UnknownHost e) {
writeLog("Unknown host: " + ((LoadRequest)p).getHostItem(), LogLevel.Debug);
writeLog(e, LogLevel.Debug);
sendInternalError(pd);
return;
} catch(LocalException e) {
sendInternalError(pd);
return;
}
} else {
writeLog("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename), LogLevel.Debug);
@ -177,6 +215,42 @@ public abstract class ServerManagement extends ServeErrors implements Runnable {
}
}
/** Send response to size request
* @param pd Request received
*/
protected < T extends ProtocolP2PPacket<?> > void sendSizeResponse(T pd) {
Payload p = pd.getPayload();
assert p instanceof SizeRequest : "payload must be an instance of SizeRequest";
if (!(p instanceof SizeRequest)) {
sendInternalError(pd);
} else {
String filename = ((SizeRequest)p).getFilename();
try {
long size = (new File(baseDirectory + filename)).length();
String[] fileList = fileListWatcher.getFileList();
if (Arrays.binarySearch(fileList, filename) >= 0) {
try {
if (size == 0) {
sendEmptyFile(pd);
} else {
pd.sendResponse(createProtocolP2PPacket((new SizeResponse(filename, size))));
}
} catch (Exception e2) {
writeLog(e2, LogLevel.Error);
}
} else {
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
*/
@ -205,6 +279,10 @@ public abstract class ServerManagement extends ServeErrors implements Runnable {
writeLog("Received LOAD_REQUEST from host " + pd.getHostItem(), LogLevel.Action);
sendLoadResponse(pd);
break;
case SIZE_REQUEST:
writeLog("Received SIZE_REQUEST from host " + pd.getHostItem(), LogLevel.Action);
sendSizeResponse(pd);
break;
case LIST_REQUEST:
writeLog("Received LIST_REQUEST from host " + pd.getHostItem(), LogLevel.Action);
sendListResponse(pd);

View File

@ -79,18 +79,22 @@ public class ServerManagementTCP extends ServerManagement {
public void run() {
writeLog("Server sucessfully started", LogLevel.Info);
fileListWatcher = (FileWatcher)new FileWatcherTCP(logger, 10000, server, tracker, baseDirectory); // checking every 10 seconds
(new Thread(fileListWatcher)).start();
Thread flwt = new Thread(fileListWatcher);
flwt.start();
fileListWatcher.setThread(flwt);
ratioWatcher = (RatioWatcher)new RatioWatcherTCP(logger, 10000, tracker);
Thread rwt = new Thread(ratioWatcher);
rwt.start();
ratioWatcher.setThread(rwt);
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);
writeLog("Socket has been closed", LogLevel.Info);
}
}
fileListWatcher.setStop();
sendUnregisterRequest();
}
/** Private runnable class allowing to serve one client.
@ -162,4 +166,13 @@ public class ServerManagementTCP extends ServerManagement {
protected Object getTrackerSocket() {
return (Object)tracker.getTCPSocket();
}
/** Closes socket */
protected void closeSocket() {
try {
socket.close();
} catch (IOException e) {
writeLog("Could not close socket", LogLevel.Error);
}
}
}

View File

@ -74,7 +74,13 @@ public class ServerManagementUDP extends ServerManagement {
public void run() {
logger.writeUDP("Server sucessfully started", LogLevel.Info);
fileListWatcher = (FileWatcher)new FileWatcherUDP(logger, 10000, server, tracker, baseDirectory); // checking every 10 seconds
(new Thread(fileListWatcher)).start();
Thread flwt = new Thread(fileListWatcher);
flwt.start();
fileListWatcher.setThread(flwt);
ratioWatcher = (RatioWatcher)new RatioWatcherUDP(logger, 10000, tracker);
Thread rwt = new Thread(ratioWatcher);
rwt.start();
ratioWatcher.setThread(rwt);
while(!stop) {
try {
ProtocolP2PPacketUDP<?> pd = new ProtocolP2PPacketUDP<>((Object)socket);
@ -83,8 +89,6 @@ public class ServerManagementUDP extends ServerManagement {
} catch (LocalException e) {
}
}
fileListWatcher.setStop();
sendUnregisterRequest();
}
@ -116,4 +120,9 @@ public class ServerManagementUDP extends ServerManagement {
protected Object getTrackerSocket() {
return (Object)tracker.getUDPSocket();
}
/** Closes socket */
protected void closeSocket() {
socket.close();
}
}

View File

@ -32,7 +32,7 @@ public class HostItem {
* @return TCP Socket
*/
public Socket getTCPSocket() {
if (tcpSocket == null) {
if (tcpSocket == null || tcpSocket.isClosed()) {
try {
tcpSocket = new Socket(InetAddress.getByName(hostname), port);
} catch (SocketException e) {
@ -56,7 +56,7 @@ public class HostItem {
* @throws IOException
*/
public Socket tryGetTCPSocket() throws SocketException, UnknownHostException, IOException {
if (tcpSocket == null) {
if (tcpSocket == null || tcpSocket.isClosed()) {
tcpSocket = new Socket(InetAddress.getByName(hostname), port);
}
return tcpSocket;
@ -79,7 +79,7 @@ public class HostItem {
* return UDP Socket
*/
public DatagramSocket getUDPSocket() {
if (udpSocket == null) {
if (udpSocket == null || udpSocket.isClosed()) {
try {
udpSocket = new DatagramSocket();
udpSocket.connect(InetAddress.getByName(hostname), port);
@ -128,7 +128,7 @@ public class HostItem {
boolean result = false;
if (other instanceof HostItem) {
HostItem that = (HostItem) other;
result = this.getHostname() == that.getHostname() && this.getPort() == that.getPort();
result = this.getHostname().equals(that.getHostname()) && this.getPort() == that.getPort();
}
return result;
}

View File

@ -34,23 +34,28 @@ public class Logger {
* @param text Text to log
*/
public void write(String text, LogLevel logLevel) {
String colorize = "\u001B[0m";
String msg = "[" + new Timestamp(System.currentTimeMillis()) + "] " + text + "\n";
String level = null;
switch (logLevel) {
case Error:
level = "[Error]";
colorize = "\u001B[31m"; // RED
break;
case Info:
level = "[Info]";
colorize = "\u001B[32m"; // GREEN
break;
case Warning:
level = "[Warning]";
colorize = "\u001B[33m"; // YELLOW
break;
case Action:
level = "[Action]";
break;
case Debug:
level = "[Debug]";
colorize = "\u001B[36m"; // CYAN
break;
default:
System.err.println("Error: incorrect logLevel");
@ -64,7 +69,7 @@ public class Logger {
case Warning:
case Debug:
default:
System.err.println(text);
System.err.println(colorize + text + "\u001B[0m");
break;
case Action:
break;

View File

@ -6,7 +6,7 @@ package tools;
* @author JS Auge
* @version 1.0
*/
public class PortRange {
public abstract class PortRange {
protected int portMax;
protected int portMin;
@ -34,7 +34,7 @@ public class PortRange {
}
/** To String
* @return String representation
* @return String representation
*/
public String toString() {
return "default " + type + "port: " + defaultPort + "(range: " + portMin + " -> " + portMax + ")";

View File

@ -79,4 +79,15 @@ public abstract class ServeErrors {
}
}
/** Send an unknown host message.
* @param pd Request received
*/
protected < T extends ProtocolP2PPacket<?> > void sendUnknownHost(T pd) {
try {
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.UNKNOWN_HOST)));
} catch (Exception e) {
writeLog(e, LogLevel.Error);
}
}
}

View File

@ -15,6 +15,9 @@ import protocolP2P.FileList;
import protocolP2P.Unregister;
import protocolP2P.Register;
import protocolP2P.RequestResponseCode;
import protocolP2P.RatioRequest;
import protocolP2P.RatioResponse;
import protocolP2P.UpdateRatio;
import localException.InternalError;
import remoteException.EmptyDirectory;
import exception.LocalException;
@ -30,6 +33,8 @@ public abstract class TrackerManagement extends ServeErrors implements Runnable
protected HostItem tracker;
protected Logger logger;
protected List<HostItem> hostList = new ArrayList<>();
protected Map<HostItem, Long> ratioUp = new HashMap<>();
protected Map<HostItem, Long> ratioDown = new HashMap<>();
protected Map<String, List<HostItem>> fileList = new HashMap<>();
protected volatile boolean stop;
@ -62,6 +67,38 @@ public abstract class TrackerManagement extends ServeErrors implements Runnable
}
}
/** Handle Ratio request
* @param pd Received request
* @throws InternalError
*/
protected < T extends ProtocolP2PPacket<?> > void handleRatio(T pd) throws InternalError {
Payload p = pd.getPayload();
assert p instanceof RatioRequest : "payload must be an instance of RatioRequest";
if (!(p instanceof RatioRequest)) {
sendInternalError(pd);
} else {
HostItem host = ((RatioRequest)p).getHostItem();
writeLog("Ratio request for host " + host, LogLevel.Debug);
try {
if (!hostList.contains(host)) {
String l = "";
for (HostItem h: hostList) {
l += h + " ";
}
writeLog(host + " is not in hostlist [ " + l + "]", LogLevel.Debug);
pd.sendResponse(createProtocolP2PPacket(new Payload(RequestResponseCode.UNKNOWN_HOST)));
} else {
pd.sendResponse(createProtocolP2PPacket(new RatioResponse(host,
ratioUp.get(host).longValue(),
ratioDown.get(host).longValue())));
}
} catch (Exception e) {
writeLog(e, LogLevel.Error);
}
}
}
/** Handle List Responses
* @param pd Received response
* @throws InternalError
@ -108,6 +145,10 @@ public abstract class TrackerManagement extends ServeErrors implements Runnable
fileList.remove(f);
}
}
/* Note: we do not remove host from ratioUp and ratioDown since Unregistering is only here to indicate
* the host is temporary not serving any files (ie. is down).
*/
}
/** Getter for HostItem socket
@ -120,6 +161,30 @@ public abstract class TrackerManagement extends ServeErrors implements Runnable
*/
protected abstract void closeHostItemSocket(HostItem hostItem);
/** Handle Update Ratio
* @param pd Received request
* @throws InternalError
*/
protected < T extends ProtocolP2PPacket<?> > void handleUpdateRatio(T pd) throws InternalError {
Payload p = pd.getPayload();
assert p instanceof UpdateRatio : "payload must be an instance of UpdateRatio";
if (!(p instanceof UpdateRatio)) {
throw new InternalError();
}
UpdateRatio updateRatio = (UpdateRatio) p;
HostItem updateRatioServer = updateRatio.getServer();
HostItem updateRatioClient = updateRatio.getClient();
long ratioSize = updateRatio.getDataSize();
writeLog("Ratio += " + ratioSize + ", client: " + updateRatioClient + " / server: " + updateRatioServer, LogLevel.Debug);
if (!ratioDown.containsKey(updateRatioClient) || ! ratioUp.containsKey(updateRatioServer)) {
sendUnknownHost(pd);
} else {
ratioDown.put(updateRatioClient, Long.valueOf(ratioDown.get(updateRatioClient).longValue() + ratioSize));
ratioUp.put(updateRatioServer, Long.valueOf(ratioUp.get(updateRatioServer).longValue() + ratioSize));
}
}
/** Handle Registering
* @param pd Received request
* @throws InternalError
@ -135,6 +200,11 @@ public abstract class TrackerManagement extends ServeErrors implements Runnable
if (!hostList.contains(host)) {
hostList.add(host);
}
// initialize ratios if this is a new host
ratioUp.putIfAbsent(host, Long.valueOf(0));
ratioDown.putIfAbsent(host, Long.valueOf(0));
// send a list request
try {
ProtocolP2PPacket<?> pLReq = createProtocolP2PPacket(new Payload(RequestResponseCode.LIST_REQUEST));
@ -186,6 +256,14 @@ public abstract class TrackerManagement extends ServeErrors implements Runnable
writeLog("Received DISCOVER REQUEST from host " + pd.getHostItem(), LogLevel.Action);
handleDiscover(pd);
break;
case RATIO_REQUEST:
writeLog("Received RATIO REQUEST from host " + pd.getHostItem(), LogLevel.Action);
handleRatio(pd);
break;
case UPDATE_RATIO:
writeLog("Received UPDATE RATIO from host " + pd.getHostItem(), LogLevel.Action);
handleUpdateRatio(pd);
break;
default:
writeLog("Received grabbage from host " + pd.getHostItem(), LogLevel.Action);
sendInternalError(pd);

View File

@ -86,11 +86,11 @@ public class TrackerManagementTCP extends TrackerManagement {
public void run() {
boolean end = false;
writeLog("[ " + addr + "] New connection", LogLevel.Action);
writeLog("[" + addr + "] New connection", LogLevel.Action);
do {
end = handleClientRequest();
} while(!end);
writeLog("[ " + addr + "] End of connection", LogLevel.Action);
writeLog("[" + addr + "] End of connection", LogLevel.Action);
}
/** Respond to next request incomming on socket s.