Ratio #86

Merged
louis_royer merged 13 commits from ratio into etape5 5 years ago

@ -2,7 +2,7 @@
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]
@ -20,12 +20,18 @@ x bytes: [(bytes 8-?): PAYLOAD]
- `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)
- `RATIO` (0x86)
- `DENIED` (0x87)
- `SIZE` (0x88)
- `VERSION ERROR` (0xC0)
- `PROTOCOL ERROR` (0xC1)
- `INTERNAL ERROR` (0xC2)
@ -33,6 +39,7 @@ x bytes: [(bytes 8-?): PAYLOAD]
- `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,18 +69,38 @@ 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
### Hash
### Size
#### Size request
Payload contains
```text
? bytes: filename
```
Possible responses: Size response, EmptyFile or NotFound
#### Size response
Payload contains
```text
8 bytes: file size
? bytes: filename
```
### 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)
@ -86,7 +112,7 @@ If file does not exists, a NotFound can be responded.
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,7 +168,7 @@ y bytes: [<FILENAME>]
Server block is composed with:
```
```text
2 bytes: [port]
? bytes: hostname
\n
@ -152,6 +178,47 @@ Server block is composed with:
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.

@ -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);

@ -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);
}

@ -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;
}

@ -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;
}

@ -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

@ -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

@ -26,6 +26,7 @@ import remoteException.NotFound;
import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.NotATracker;
import remoteException.UnknownHost;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
@ -46,6 +47,7 @@ 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;
@ -56,13 +58,15 @@ public abstract class ClientManagement extends ServeErrors implements Runnable {
* @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) {
@ -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();

@ -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);
}

@ -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

@ -178,7 +178,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 +186,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;
}

@ -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;
}
}

@ -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

@ -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

@ -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;
}
}

@ -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

@ -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;

@ -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;
}
}

@ -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();
}

@ -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

@ -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;

@ -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;

@ -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;
}
}

@ -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;
}
}

@ -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

@ -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;

@ -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;
}
}

@ -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;
}
}

@ -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

@ -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;
}
}

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

@ -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;
}
}

@ -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();
}

@ -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();
}
}

@ -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();
}
}

@ -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);

@ -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);
}
}
}

@ -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();
}
}

@ -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;
}

@ -34,7 +34,7 @@ public class Logger {
* @param text Text to log
*/
public void write(String text, LogLevel logLevel) {
String colorize = "\u001B[0";
String colorize = "\u001B[0m";
String msg = "[" + new Timestamp(System.currentTimeMillis()) + "] " + text + "\n";
String level = null;
switch (logLevel) {
@ -55,6 +55,7 @@ public class Logger {
break;
case Debug:
level = "[Debug]";
colorize = "\u001B[36m"; // CYAN
break;
default:
System.err.println("Error: incorrect logLevel");

@ -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);
}
}
}

@ -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);

@ -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.

Loading…
Cancel
Save