Merge branch 'WIP_etape1' of flavien/Projet_JAVA_P2P_STRI2A into master
Fin de l’étape 1
This commit is contained in:
commit
3fc24dfef6
104
.gitignore
vendored
104
.gitignore
vendored
@ -1,4 +1,108 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
*.swp
|
*.swp
|
||||||
|
*.class
|
||||||
bin
|
bin
|
||||||
project.geany
|
project.geany
|
||||||
|
P2P_JAVA_PROJECT_SERVER
|
||||||
|
P2P_JAVA_PROJECT_CLIENT
|
||||||
|
.classpath
|
||||||
|
.project
|
||||||
|
|
||||||
|
# Created by https://www.gitignore.io/api/java,eclipse
|
||||||
|
# Edit at https://www.gitignore.io/?templates=java,eclipse
|
||||||
|
|
||||||
|
### Eclipse ###
|
||||||
|
.metadata
|
||||||
|
bin/
|
||||||
|
tmp/
|
||||||
|
*.tmp
|
||||||
|
*.bak
|
||||||
|
*.swp
|
||||||
|
*~.nib
|
||||||
|
local.properties
|
||||||
|
.settings/
|
||||||
|
.loadpath
|
||||||
|
.recommenders
|
||||||
|
|
||||||
|
# External tool builders
|
||||||
|
.externalToolBuilders/
|
||||||
|
|
||||||
|
# Locally stored "Eclipse launch configurations"
|
||||||
|
*.launch
|
||||||
|
|
||||||
|
# PyDev specific (Python IDE for Eclipse)
|
||||||
|
*.pydevproject
|
||||||
|
|
||||||
|
# CDT-specific (C/C++ Development Tooling)
|
||||||
|
.cproject
|
||||||
|
|
||||||
|
# CDT- autotools
|
||||||
|
.autotools
|
||||||
|
|
||||||
|
# Java annotation processor (APT)
|
||||||
|
.factorypath
|
||||||
|
|
||||||
|
# PDT-specific (PHP Development Tools)
|
||||||
|
.buildpath
|
||||||
|
|
||||||
|
# sbteclipse plugin
|
||||||
|
.target
|
||||||
|
|
||||||
|
# Tern plugin
|
||||||
|
.tern-project
|
||||||
|
|
||||||
|
# TeXlipse plugin
|
||||||
|
.texlipse
|
||||||
|
|
||||||
|
# STS (Spring Tool Suite)
|
||||||
|
.springBeans
|
||||||
|
|
||||||
|
# Code Recommenders
|
||||||
|
.recommenders/
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated/
|
||||||
|
|
||||||
|
# Scala IDE specific (Scala & Java development for Eclipse)
|
||||||
|
.cache-main
|
||||||
|
.scala_dependencies
|
||||||
|
.worksheet
|
||||||
|
|
||||||
|
### Eclipse Patch ###
|
||||||
|
# Eclipse Core
|
||||||
|
.project
|
||||||
|
|
||||||
|
# JDT-specific (Eclipse Java Development Tools)
|
||||||
|
.classpath
|
||||||
|
|
||||||
|
# Annotation Processing
|
||||||
|
.apt_generated
|
||||||
|
|
||||||
|
.sts4-cache/
|
||||||
|
|
||||||
|
### Java ###
|
||||||
|
# Compiled class file
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.nar
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
# End of https://www.gitignore.io/api/java,eclipse
|
||||||
|
File diff suppressed because one or more lines are too long
82
doc/protocol.md
Normal file
82
doc/protocol.md
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
# P2P-JAVA-PROJECT version 1 (Protocol for step 1)
|
||||||
|
All messages begins with `P2P-JAVA-PROJECT VERSION 1.0\n` (this version of the protocol).
|
||||||
|
|
||||||
|
## Client messages
|
||||||
|
- `LIST\n`: ask the server to list files from server root directory
|
||||||
|
- `DOWNLOAD\n<FILENAME>\n`: ask the server to download file <FILENAME> from server root directory. Only one filename is allowed per request.
|
||||||
|
|
||||||
|
## Server responses
|
||||||
|
- The response to `LIST` request is in the format `LIST\n<FILENAME 1>\n<FILENAME 2>\n[…]<LAST FILENAME>\n\n`
|
||||||
|
- The response to `DOWNLOAD` request is `LOAD\n<FILESIZE (BYTES)>\n<POSITION>\n<CONTENT OF FILE>` or `NOT FOUND\n` if the file doesn't exists.
|
||||||
|
- The server send a `PROTOCOL ERROR\n` message if it doesn't understands what the client sent.
|
||||||
|
- The server send a `INTERNAL ERROR\n` message if it encounters an internal error.
|
||||||
|
|
||||||
|
# P2P-JAVA-PROJECT version 1.1 (Binary protocol for step 1)
|
||||||
|
|
||||||
|
All strings in the datagram are utf-8 encoded.
|
||||||
|
|
||||||
|
```Datagram format
|
||||||
|
|
||||||
|
1 byte: [0-7: VERSION(0x11, first quartet is major version, second is minor)]
|
||||||
|
1 byte: [8-15: REQUEST/RESPONSE CODE]
|
||||||
|
2 bytes: [16-31: RESERVED FOR FUTURE USE]
|
||||||
|
4 bytes: [32-63: PAYLOAD SIZE IN BYTES]
|
||||||
|
x bytes: [64-xx: PAYLOAD]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Requests and responses codes
|
||||||
|
- REQUESTS (msb is 0):
|
||||||
|
- `LIST` (0x00)
|
||||||
|
- `LOAD` (0x01)
|
||||||
|
|
||||||
|
- RESPONSES (msb is 1):
|
||||||
|
- `LIST` (0x80)
|
||||||
|
- `LOAD` (0x81)
|
||||||
|
- `VERSION ERROR` (0xC0)
|
||||||
|
- `PROTOCOL ERROR` (0xC1)
|
||||||
|
- `INTERNAL ERROR` (0xC2)
|
||||||
|
- `EMPTY DIRECTORY` (0xC3)
|
||||||
|
- `NOT FOUND` (0xC4)
|
||||||
|
- `EMPTY FILE` (0xC5)
|
||||||
|
|
||||||
|
### List
|
||||||
|
Payload size for list request is always zero.
|
||||||
|
Payload for list response is filenames separated by `\n`. Payload size for list response is never zero.
|
||||||
|
|
||||||
|
#### Empty directory
|
||||||
|
When directory is empty.
|
||||||
|
Payload size for empty directory is always zero.
|
||||||
|
|
||||||
|
|
||||||
|
### Load
|
||||||
|
#### Not found
|
||||||
|
Response when the file requested is not found on the server.
|
||||||
|
Payload size for Not found is zero.
|
||||||
|
|
||||||
|
|
||||||
|
#### Load response
|
||||||
|
Payload contains
|
||||||
|
|
||||||
|
```
|
||||||
|
8 bytes: [64-127: OFFSET OF FILE CONTENT IN BYTES]
|
||||||
|
8 bytes: [128-191: TOTAL FILESIZE]
|
||||||
|
4 bytes: [192-223: FILENAME SIZE] (cannot be > to PAYLOAD_SIZE - 20 or be zero)
|
||||||
|
y bytes: [<FILENAME>]
|
||||||
|
z bytes: [FILE CONTENT]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Load request
|
||||||
|
Payload contains only the name of the file to load.
|
||||||
|
|
||||||
|
### Other response code (errors)
|
||||||
|
#### Version error
|
||||||
|
Response when datagram received use wrong version code.
|
||||||
|
|
||||||
|
#### Protocol error
|
||||||
|
Response when the request cannot be interpreted (but version is correct).
|
||||||
|
Payload size for Protocol error is zero
|
||||||
|
|
||||||
|
#### Internal error
|
||||||
|
Response in internal failure case.
|
||||||
|
Payload size for Internal error is zero.
|
179
src/clientP2P/ClientManagementUDP.java
Normal file
179
src/clientP2P/ClientManagementUDP.java
Normal file
@ -0,0 +1,179 @@
|
|||||||
|
package clientP2P;
|
||||||
|
import exception.InternalError;
|
||||||
|
import exception.ProtocolError;
|
||||||
|
import exception.SizeError;
|
||||||
|
import exception.TransmissionError;
|
||||||
|
import exception.VersionError;
|
||||||
|
import remoteException.EmptyFile;
|
||||||
|
import remoteException.EmptyDirectory;
|
||||||
|
import remoteException.InternalRemoteError;
|
||||||
|
import remoteException.NotFound;
|
||||||
|
import remoteException.ProtocolRemoteError;
|
||||||
|
import remoteException.VersionRemoteError;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.io.File;
|
||||||
|
import protocolP2P.ProtocolP2PDatagram;
|
||||||
|
import protocolP2P.Payload;
|
||||||
|
import protocolP2P.RequestResponseCode;
|
||||||
|
import protocolP2P.FileList;
|
||||||
|
import protocolP2P.FilePart;
|
||||||
|
import protocolP2P.LoadRequest;
|
||||||
|
|
||||||
|
/** Implementation of P2P-JAVA-PROJECT CLIENT
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public class ClientManagementUDP implements Runnable {
|
||||||
|
private String baseDirectory;
|
||||||
|
private int UDPPort;
|
||||||
|
private String host;
|
||||||
|
private DatagramSocket socket;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Constructor for UDP implementation, with baseDirectory and UDPPort parameters.
|
||||||
|
* @param baseDirectory the root directory where files are stored
|
||||||
|
* @param host hostname of the server
|
||||||
|
* @param UDPPort the server will listen on this port
|
||||||
|
*/
|
||||||
|
public ClientManagementUDP(String baseDirectory, String host, int UDPPort) {
|
||||||
|
this.baseDirectory = baseDirectory;
|
||||||
|
this.host = host;
|
||||||
|
this.UDPPort = UDPPort;
|
||||||
|
try {
|
||||||
|
socket = new DatagramSocket();
|
||||||
|
} catch (SocketException e) {
|
||||||
|
System.err.println("Error: No socket available.");
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implementation of Runnable
|
||||||
|
*/
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
String[] list = listDirectory();
|
||||||
|
System.out.println("Files present on the server:");
|
||||||
|
for(String listItem: list) {
|
||||||
|
System.out.println(listItem);
|
||||||
|
}
|
||||||
|
System.out.println("Name of the file to download:");
|
||||||
|
Scanner scanner = new Scanner(System.in);
|
||||||
|
String f = scanner.nextLine();
|
||||||
|
download(f);
|
||||||
|
System.out.println("File sucessfully downloaded");
|
||||||
|
} catch (EmptyDirectory e) {
|
||||||
|
System.err.println("Error: Server has no file in directory");
|
||||||
|
} catch (InternalError e) {
|
||||||
|
System.err.println("Error: Client internal error");
|
||||||
|
} catch (UnknownHostException e) {
|
||||||
|
System.err.println("Error: Server host is unknown");
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("Error: Request cannot be send or response cannot be received");
|
||||||
|
} catch (TransmissionError e) {
|
||||||
|
System.err.println("Error: Message received is too big");
|
||||||
|
} catch (ProtocolError e) {
|
||||||
|
System.err.println("Error: Cannot decode server’s response");
|
||||||
|
} catch (VersionError e) {
|
||||||
|
System.err.println("Error: Server’s response use bad version of the protocol");
|
||||||
|
} catch (SizeError e) {
|
||||||
|
System.err.println("Error: Cannot handle this packets because of internal representation limitations of numbers on the client");
|
||||||
|
} catch (InternalRemoteError e) {
|
||||||
|
System.err.println("Error: Server internal error");
|
||||||
|
} catch (ProtocolRemoteError e) {
|
||||||
|
System.err.println("Error: Server cannot decode client’s request");
|
||||||
|
} catch (VersionRemoteError e) {
|
||||||
|
System.err.println("Error: Server cannot decode this version of the protocol");
|
||||||
|
} catch (NotFound e) {
|
||||||
|
System.err.println("Error: Server has not this file in directory");
|
||||||
|
} catch (EmptyFile e) {
|
||||||
|
System.err.println("Error: File is empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Try to download a file
|
||||||
|
* @param filename name of the file to download
|
||||||
|
* @throws NotFound
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws UnknownHostException
|
||||||
|
* @throws IOException
|
||||||
|
* @throws TransmissionError
|
||||||
|
* @throws ProtocolError
|
||||||
|
* @throws VersionError
|
||||||
|
* @throws SizeError
|
||||||
|
* @throws InternalRemoteError
|
||||||
|
* @throws ProtocolRemoteError
|
||||||
|
* @throws VersionRemoteError
|
||||||
|
* @throws EmptyFile
|
||||||
|
*/
|
||||||
|
private void download(String filename) throws EmptyFile, NotFound, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError {
|
||||||
|
ProtocolP2PDatagram d = new ProtocolP2PDatagram((Payload) new LoadRequest(filename));
|
||||||
|
d.send(socket, host, UDPPort);
|
||||||
|
try {
|
||||||
|
Payload p = ProtocolP2PDatagram.receive(socket).getPayload();
|
||||||
|
assert p instanceof FilePart : "This payload must be instance of FilePart";
|
||||||
|
if (!(p instanceof FilePart)) {
|
||||||
|
throw new InternalError();
|
||||||
|
} else {
|
||||||
|
FilePart fp = (FilePart)p;
|
||||||
|
if (!fp.getFilename().equals(filename)) {
|
||||||
|
System.err.println("Error: wrong file received");
|
||||||
|
throw new ProtocolError();
|
||||||
|
}
|
||||||
|
if (fp.getOffset() != 0 || fp.getPartialContent().length != fp.getTotalSize()) {
|
||||||
|
System.err.println("offset: " + fp.getOffset() + " ; content.length: " + fp.getPartialContent().length + " ; totalSize: " + fp.getTotalSize());
|
||||||
|
System.err.println("Error: cannot handle partial files (not implemented)");
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Files.write(new File(baseDirectory + filename).toPath(), fp.getPartialContent());
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("Error: cannot write file (" + baseDirectory + filename + ")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (EmptyDirectory e) {
|
||||||
|
throw new ProtocolError();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** list server’s directory content
|
||||||
|
* @return list of files
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws UnknowHostException
|
||||||
|
* @throws IOException
|
||||||
|
* @throws TransmissionError
|
||||||
|
* @throws ProtocolError
|
||||||
|
* @throws VersionError
|
||||||
|
* @throws SizeError
|
||||||
|
* @throws EmptyDirectory
|
||||||
|
* @throws InternalRemoteError
|
||||||
|
* @throws ProtocolRemoteError
|
||||||
|
* @throws VersionRemoteError
|
||||||
|
*/
|
||||||
|
private String[] listDirectory() throws EmptyDirectory, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError {
|
||||||
|
ProtocolP2PDatagram d = new ProtocolP2PDatagram(new Payload(RequestResponseCode.LIST_REQUEST));
|
||||||
|
d.send(socket, host, UDPPort);
|
||||||
|
try {
|
||||||
|
Payload p = ProtocolP2PDatagram.receive(socket).getPayload();
|
||||||
|
assert p instanceof FileList : "This payload must be instance of Filelist";
|
||||||
|
if (!(p instanceof FileList)) {
|
||||||
|
throw new InternalError();
|
||||||
|
} else {
|
||||||
|
return ((FileList)p).getFileList();
|
||||||
|
}
|
||||||
|
} catch (NotFound e) {
|
||||||
|
throw new ProtocolError();
|
||||||
|
} catch (EmptyFile e) {
|
||||||
|
throw new ProtocolError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
src/clientP2P/ClientP2P.java
Normal file
25
src/clientP2P/ClientP2P.java
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package clientP2P;
|
||||||
|
import clientP2P.ClientManagementUDP;
|
||||||
|
import tools.Directories;
|
||||||
|
|
||||||
|
|
||||||
|
public class ClientP2P {
|
||||||
|
private String host;
|
||||||
|
private int port;
|
||||||
|
private Directories directories;
|
||||||
|
public ClientP2P() {
|
||||||
|
directories = new Directories("P2P_JAVA_PROJECT_CLIENT");
|
||||||
|
host = "localhost";
|
||||||
|
port = 40001;
|
||||||
|
System.out.println("Client will try to contact server at " + host + " on port " + port + ". It will save files in " + directories.getDataHomeDirectory());
|
||||||
|
directories.askOpenDataHomeDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String [] args) {
|
||||||
|
ClientP2P c = new ClientP2P();
|
||||||
|
ClientManagementUDP cm = new ClientManagementUDP(c.directories.getDataHomeDirectory(), c.host, c.port);
|
||||||
|
Thread t = new Thread(cm);
|
||||||
|
t.setName("client P2P-JAVA-PROJECT");
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
}
|
4
src/exception/InternalError.java
Normal file
4
src/exception/InternalError.java
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package exception;
|
||||||
|
public class InternalError extends Exception {
|
||||||
|
private static final long serialVersionUID = 11L;
|
||||||
|
}
|
4
src/exception/ProtocolError.java
Normal file
4
src/exception/ProtocolError.java
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package exception;
|
||||||
|
public class ProtocolError extends Exception {
|
||||||
|
private static final long serialVersionUID = 11L;
|
||||||
|
}
|
5
src/exception/SizeError.java
Normal file
5
src/exception/SizeError.java
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package exception;
|
||||||
|
/** Used on reception side when size as set in datagram is too big, and we cant store this in a int/long as usual. */
|
||||||
|
public class SizeError extends Exception {
|
||||||
|
private static final long serialVersionUID = 11L;
|
||||||
|
}
|
4
src/exception/TransmissionError.java
Normal file
4
src/exception/TransmissionError.java
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package exception;
|
||||||
|
public class TransmissionError extends Exception {
|
||||||
|
private static final long serialVersionUID = 11L;
|
||||||
|
}
|
4
src/exception/VersionError.java
Normal file
4
src/exception/VersionError.java
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package exception;
|
||||||
|
public class VersionError extends Exception {
|
||||||
|
private static final long serialVersionUID = 11L;
|
||||||
|
}
|
13
src/protocolP2P/CodeType.java
Normal file
13
src/protocolP2P/CodeType.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package protocolP2P;
|
||||||
|
|
||||||
|
/** Request/Response code's type enum.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public enum CodeType {
|
||||||
|
REQUEST,
|
||||||
|
RESPONSE,
|
||||||
|
ERROR
|
||||||
|
}
|
109
src/protocolP2P/FileList.java
Normal file
109
src/protocolP2P/FileList.java
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
package protocolP2P;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import protocolP2P.Payload;
|
||||||
|
import protocolP2P.RequestResponseCode;
|
||||||
|
import exception.TransmissionError;
|
||||||
|
import exception.ProtocolError;
|
||||||
|
import exception.InternalError;
|
||||||
|
import exception.SizeError;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
/** Representation of payload for list response.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public class FileList extends Payload {
|
||||||
|
private String[] fileList;
|
||||||
|
|
||||||
|
/** Constructor (typically used by the server) with an ArrayList parameter containing
|
||||||
|
* filenames.
|
||||||
|
* @param fileList a list of files. Must not be empty.
|
||||||
|
* @throws InternalError
|
||||||
|
*/
|
||||||
|
public FileList(String[] fileList) throws InternalError {
|
||||||
|
super(RequestResponseCode.LIST_RESPONSE);
|
||||||
|
/* assert to help debugging */
|
||||||
|
assert fileList.length != 0 : "Payload size of FileList must not be empty, use EmptyDirectory from Payload instead";
|
||||||
|
if (fileList.length == 0) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
this.fileList = fileList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor (typically used by client) with a byte[] parameter containing the datagram received.
|
||||||
|
* @param datagram the full datagram received
|
||||||
|
* @throws SizeError
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws ProtocolError
|
||||||
|
* @throws TransmissionError
|
||||||
|
*/
|
||||||
|
protected FileList(byte[] datagram) throws TransmissionError, SizeError, ProtocolError, InternalError {
|
||||||
|
super(datagram);
|
||||||
|
/* assert to help debugging */
|
||||||
|
assert requestResponseCode == RequestResponseCode.LIST_RESPONSE : "FileList subclass is incompatible with this datagram, request/response code must be checked before using this constructor";
|
||||||
|
/* InternalErrorException */
|
||||||
|
if (requestResponseCode!= RequestResponseCode.LIST_RESPONSE) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
int size = getPayloadSize(datagram);
|
||||||
|
try {
|
||||||
|
fileList = (new String(datagram, 8, size, "UTF-8")).split("\n");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a byte[] containing datagram with padding.
|
||||||
|
* This datagram is still incomplete and should not be send directly.
|
||||||
|
* ProtocolP2PDatagram will use this method to generate the complete datagram.
|
||||||
|
* @return datagram with padding
|
||||||
|
* @throws InternalError
|
||||||
|
*/
|
||||||
|
protected byte[] toDatagram() throws InternalError {
|
||||||
|
// compute size
|
||||||
|
int size = 8;
|
||||||
|
for (String s : fileList) {
|
||||||
|
size += s.length();
|
||||||
|
size += 1;
|
||||||
|
}
|
||||||
|
size -=1;
|
||||||
|
byte[] datagram = new byte[size]; // java initialize all to zero
|
||||||
|
// set request/response code
|
||||||
|
datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||||||
|
// bits 16-31 are reserved for future use
|
||||||
|
setPayloadSize(size - 8, datagram);
|
||||||
|
// Write fileList
|
||||||
|
int bCount = 8;
|
||||||
|
for(String s : fileList) {
|
||||||
|
if (bCount != 8) { // not on first iteration
|
||||||
|
try {
|
||||||
|
datagram[bCount] = "\n".getBytes("UTF-8")[0]; // separator
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
bCount += 1;
|
||||||
|
}
|
||||||
|
// Copy filename
|
||||||
|
try {
|
||||||
|
byte[] sb = s.getBytes("UTF-8");
|
||||||
|
for(byte b : sb) {
|
||||||
|
datagram[bCount] = b;
|
||||||
|
bCount += 1;
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return datagram;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** fileList getter.
|
||||||
|
* @return fileList
|
||||||
|
*/
|
||||||
|
public String[] getFileList() {
|
||||||
|
return fileList;
|
||||||
|
}
|
||||||
|
}
|
199
src/protocolP2P/FilePart.java
Normal file
199
src/protocolP2P/FilePart.java
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
package protocolP2P;
|
||||||
|
import protocolP2P.Payload;
|
||||||
|
import protocolP2P.RequestResponseCode;
|
||||||
|
import exception.ProtocolError;
|
||||||
|
import exception.InternalError;
|
||||||
|
import exception.SizeError;
|
||||||
|
import exception.TransmissionError;
|
||||||
|
import tools.BytesArrayTools;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
/** Representation of payload for load response.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public class FilePart extends Payload {
|
||||||
|
private String filename;
|
||||||
|
private long totalSize;
|
||||||
|
private long offset;
|
||||||
|
private byte[] partialContent;
|
||||||
|
|
||||||
|
/** Constructor (typically used by server) with informations about file part to send as parameters.
|
||||||
|
* @param filename name of the file to send
|
||||||
|
* @param totalSize total size of the file to send
|
||||||
|
* @param offset where in the file begins the part we are sending
|
||||||
|
* @param partialContent content of the file we send
|
||||||
|
* @throws InternalError
|
||||||
|
*/
|
||||||
|
public FilePart(String filename, long totalSize, 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) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
this.filename = filename;
|
||||||
|
this.totalSize = totalSize;
|
||||||
|
this.offset = offset;
|
||||||
|
this.partialContent = partialContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor (typically used by client) with datagram received as parameter.
|
||||||
|
* @param datagram the full datagram received
|
||||||
|
* @throws SizeError
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws TransmissionError
|
||||||
|
*/
|
||||||
|
protected FilePart(byte[] datagram) throws TransmissionError, SizeError, ProtocolError, InternalError {
|
||||||
|
super(datagram);
|
||||||
|
/* assert to help debugging */
|
||||||
|
assert requestResponseCode == RequestResponseCode.LOAD_RESPONSE : "FilePart subclass is incompatible with this datagram, request/response code must be checked before using this constructor";
|
||||||
|
/* InternalErrorException */
|
||||||
|
if (requestResponseCode != RequestResponseCode.LOAD_RESPONSE) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
setOffset(datagram); // this can throw SizeError
|
||||||
|
setTotalSize(datagram); // this can throw SizeError
|
||||||
|
setFilename(datagram); // this can throw ProtocolError, SizeError
|
||||||
|
setPartialContent(datagram); // this can throw SizeError
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a byte[] containing datagram with padding.
|
||||||
|
* This datagram is still incomplete and should not be send directly.
|
||||||
|
* ProtocolP2PDatagram will use this method to generate the complete datagram.
|
||||||
|
* @return datagram with padding
|
||||||
|
* @throws InternalError
|
||||||
|
*/
|
||||||
|
protected byte[] toDatagram() throws InternalError {
|
||||||
|
// compute payload size
|
||||||
|
int size = 28 + filename.length() + partialContent.length;
|
||||||
|
byte[] datagram = new byte[size]; // java initialize all to zero
|
||||||
|
// set request/response code
|
||||||
|
datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||||||
|
// bits 16-31 are reserved for future use
|
||||||
|
setPayloadSize(size - 8, datagram);
|
||||||
|
// write offset to datagram (Byte 8)
|
||||||
|
BytesArrayTools.write(datagram, 8, offset);
|
||||||
|
// write totalSize to datagram (Byte 16)
|
||||||
|
BytesArrayTools.write(datagram, 16, totalSize);
|
||||||
|
// write filename’s size to datagram
|
||||||
|
BytesArrayTools.write(datagram, 24, filename.length());
|
||||||
|
// write filename to datagram
|
||||||
|
try {
|
||||||
|
byte[] bFilename = filename.getBytes("UTF-8");
|
||||||
|
int i = filename.length() + 24;
|
||||||
|
for (byte b : bFilename) {
|
||||||
|
datagram[i] = b;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
// write partialContent to datagram
|
||||||
|
for (byte b: partialContent) {
|
||||||
|
datagram[i] = b;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
return datagram;
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write from bytes 8 to 15 of datagram into offset.
|
||||||
|
* @param datagram received datagram
|
||||||
|
* @throws SizeError
|
||||||
|
*/
|
||||||
|
private void setOffset(byte[] datagram) throws SizeError {
|
||||||
|
offset = BytesArrayTools.readLong(datagram, 8);
|
||||||
|
}
|
||||||
|
/** Write from bytes 16 to 23 of datagram into totalSize.
|
||||||
|
* @param datagram received datagram
|
||||||
|
* @throws SizeError
|
||||||
|
*/
|
||||||
|
private void setTotalSize(byte[] datagram) throws SizeError {
|
||||||
|
totalSize = BytesArrayTools.readLong(datagram, 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read filename’s size from bytes 24 to 27 of datagram.
|
||||||
|
* @param datagram received datagram
|
||||||
|
* @throws ProtocolError
|
||||||
|
* @throws SizeError
|
||||||
|
* @return filename’s size
|
||||||
|
*/
|
||||||
|
private int getFilenameSize(byte[] datagram) throws SizeError, ProtocolError {
|
||||||
|
int size = BytesArrayTools.readInt(datagram, 24); // this can throw SizeError
|
||||||
|
// filename size cannot be zero
|
||||||
|
if (size == 0) {
|
||||||
|
throw new ProtocolError();
|
||||||
|
}
|
||||||
|
// offset (8B) + totalSize (8B) + filenameSize (4B) = 20B
|
||||||
|
if ((20 + size) > getPayloadSize(datagram)) {
|
||||||
|
throw new ProtocolError();
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write filename from byte 28 to byte (28 + (filenameSize - 1)) of datagram.
|
||||||
|
* @param datagram received datagram
|
||||||
|
* @throws ProtocolError
|
||||||
|
* @throws SizeError
|
||||||
|
* @throws InternalError
|
||||||
|
*/
|
||||||
|
private void setFilename(byte[] datagram) throws ProtocolError, SizeError, InternalError {
|
||||||
|
int filenameSize = getFilenameSize(datagram); // this can throw ProtocolError or SizeError
|
||||||
|
try {
|
||||||
|
filename = new String(datagram, 28, filenameSize, "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Write partialContent from byte (28 + filenameSize) to byte (8 + payloadSize) of datagram.
|
||||||
|
* @param datagram received datagram
|
||||||
|
* @throws SizeError
|
||||||
|
* @throws ProtocolError
|
||||||
|
*/
|
||||||
|
private void setPartialContent(byte[] datagram) throws ProtocolError, SizeError {
|
||||||
|
int start = 28 + getFilenameSize(datagram); // this can throw SizeError or ProtocolError
|
||||||
|
int end = 8 + getPayloadSize(datagram); // this can throw SizeError
|
||||||
|
try {
|
||||||
|
partialContent = Arrays.copyOfRange(datagram, start, end);
|
||||||
|
} catch (ArrayIndexOutOfBoundsException e) {
|
||||||
|
throw new ProtocolError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** partialContent getter.
|
||||||
|
* @return partialcontent
|
||||||
|
*/
|
||||||
|
public byte[] getPartialContent() {
|
||||||
|
return partialContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** filename getter.
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** offset getter.
|
||||||
|
* @return offset
|
||||||
|
*/
|
||||||
|
public long getOffset() {
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** totalSize getter.
|
||||||
|
* @return totalSize
|
||||||
|
*/
|
||||||
|
public long getTotalSize() {
|
||||||
|
return totalSize;
|
||||||
|
}
|
||||||
|
}
|
90
src/protocolP2P/LoadRequest.java
Normal file
90
src/protocolP2P/LoadRequest.java
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
package protocolP2P;
|
||||||
|
import protocolP2P.Payload;
|
||||||
|
import protocolP2P.RequestResponseCode;
|
||||||
|
import exception.TransmissionError;
|
||||||
|
import exception.ProtocolError;
|
||||||
|
import exception.InternalError;
|
||||||
|
import exception.SizeError;
|
||||||
|
import java.io.UnsupportedEncodingException;
|
||||||
|
|
||||||
|
/** Representation of payload for load request.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public class LoadRequest extends Payload {
|
||||||
|
private String filename;
|
||||||
|
|
||||||
|
/** Constructor (typically used by the server) with a filename parameter.
|
||||||
|
* @param filename name of the file to download. Must not be empty.
|
||||||
|
* @throws InternalError
|
||||||
|
*/
|
||||||
|
public LoadRequest(String filename) throws InternalError {
|
||||||
|
super(RequestResponseCode.LOAD_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 client) with a byte[] parameter containing the datagram received.
|
||||||
|
* @param datagram the full datagram received
|
||||||
|
* @throws SizeError
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws ProtocolError
|
||||||
|
* @throws TransmissionError
|
||||||
|
*/
|
||||||
|
protected LoadRequest(byte[] datagram) throws TransmissionError, SizeError, ProtocolError, InternalError {
|
||||||
|
super(datagram);
|
||||||
|
/* assert to help debugging */
|
||||||
|
assert requestResponseCode == RequestResponseCode.LOAD_REQUEST : "LoadRequest subclass is incompatible with this datagram, request/response code must be checked before using this constructor";
|
||||||
|
/* InternalErrorException */
|
||||||
|
if (requestResponseCode!= RequestResponseCode.LOAD_REQUEST) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
int size = getPayloadSize(datagram);
|
||||||
|
try {
|
||||||
|
filename = new String(datagram, 8, size, "UTF-8");
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a byte[] containing datagram with padding.
|
||||||
|
* This datagram is still incomplete and should not be send directly.
|
||||||
|
* ProtocolP2PDatagram will use this method to generate the complete datagram.
|
||||||
|
* @return datagram with padding
|
||||||
|
* @throws InternalError
|
||||||
|
*/
|
||||||
|
protected byte[] toDatagram() throws InternalError {
|
||||||
|
// compute size
|
||||||
|
int size = 8 + filename.length();
|
||||||
|
byte[] datagram = new byte[size]; // java initialize all to zero
|
||||||
|
// set request/response code
|
||||||
|
datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||||||
|
// bits 16-31 are reserved for future use
|
||||||
|
setPayloadSize(size - 8, datagram);
|
||||||
|
// Write filename
|
||||||
|
int bCount = 8;
|
||||||
|
try {
|
||||||
|
byte[] sb = filename.getBytes("UTF-8");
|
||||||
|
for(byte b : sb) {
|
||||||
|
datagram[bCount] = b;
|
||||||
|
bCount += 1;
|
||||||
|
}
|
||||||
|
} catch (UnsupportedEncodingException e) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
return datagram;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** filename getter.
|
||||||
|
* @return filename
|
||||||
|
*/
|
||||||
|
public String getFilename() {
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
}
|
116
src/protocolP2P/Payload.java
Normal file
116
src/protocolP2P/Payload.java
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package protocolP2P;
|
||||||
|
import protocolP2P.RequestResponseCode;
|
||||||
|
import protocolP2P.FilePart;
|
||||||
|
import protocolP2P.FileList;
|
||||||
|
import protocolP2P.LoadRequest;
|
||||||
|
import exception.ProtocolError;
|
||||||
|
import exception.InternalError;
|
||||||
|
import exception.TransmissionError;
|
||||||
|
import exception.SizeError;
|
||||||
|
import tools.BytesArrayTools;
|
||||||
|
/** Representation of payload. If payload has a size, use subclasses instead.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public class Payload {
|
||||||
|
protected RequestResponseCode requestResponseCode;
|
||||||
|
protected final static int PAYLOAD_SIZE_POSITION = 4;
|
||||||
|
protected final static int PAYLOAD_START_POSITION = 8;
|
||||||
|
|
||||||
|
/** Consructor used to create Payload with a payload size of zero using a RRCode.
|
||||||
|
* @param requestResponseCode Request/Response code associated with the payload
|
||||||
|
* @throws InternalError
|
||||||
|
*/
|
||||||
|
public Payload(RequestResponseCode requestResponseCode) throws InternalError {
|
||||||
|
/* asserts to help debugging */
|
||||||
|
assert requestResponseCode != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class";
|
||||||
|
assert requestResponseCode != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class";
|
||||||
|
assert requestResponseCode != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class";
|
||||||
|
this.requestResponseCode = requestResponseCode;
|
||||||
|
checkRequestResponseCode(); // this can throw InternalError
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Constructor used to create a Payload (when no more specific subclasses exists) using datagram as parameter.
|
||||||
|
* If payload size is not empty, using subclass is required.
|
||||||
|
* @param datagram the full datagram received
|
||||||
|
* @throws ProtocolError
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws TransmissionError
|
||||||
|
* @throws SizeError
|
||||||
|
*/
|
||||||
|
protected Payload(byte[] datagram) throws SizeError, ProtocolError, InternalError, TransmissionError {
|
||||||
|
/* asserts to help debugging */
|
||||||
|
assert getPayloadSize(datagram) + 8 <= datagram.length : "Payload is truncated";
|
||||||
|
if (datagram.length < getPayloadSize(datagram) + 8) {
|
||||||
|
throw new TransmissionError();
|
||||||
|
}
|
||||||
|
assert RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class";
|
||||||
|
assert RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class";
|
||||||
|
assert RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class";
|
||||||
|
requestResponseCode = RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]);
|
||||||
|
checkRequestResponseCode(); // this can throw InternalError
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Used to check RRCode used is compatible with this class use, or if a more specific subclass is required.
|
||||||
|
* @throws InternalError
|
||||||
|
*/
|
||||||
|
private void checkRequestResponseCode() throws InternalError {
|
||||||
|
/* Incorrect use cases (use subclasses instead) */
|
||||||
|
if ((requestResponseCode == RequestResponseCode.LIST_RESPONSE && !(this instanceof FileList))
|
||||||
|
|| (requestResponseCode == RequestResponseCode.LOAD_RESPONSE && !(this instanceof FilePart))
|
||||||
|
|| (requestResponseCode == RequestResponseCode.LOAD_REQUEST && !(this instanceof LoadRequest))) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a byte[] containing datagram with padding.
|
||||||
|
* This datagram is still incomplete and should not be send directly.
|
||||||
|
* ProtocolP2PDatagram will use this method to generate the complete datagram.
|
||||||
|
* @return datagram with padding
|
||||||
|
* @throws InternalError
|
||||||
|
*/
|
||||||
|
protected byte[] toDatagram() throws InternalError {
|
||||||
|
// InternalError is impossible in this method on Payload class but still on subclasses
|
||||||
|
byte [] datagram = new byte[8]; // java initialize all to zero
|
||||||
|
// size is zero (and this is the default)
|
||||||
|
// set request/response code
|
||||||
|
datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||||||
|
// bits 16-31 are reserved for future use
|
||||||
|
// payload size is 0 (this is what java have initialized datagram)
|
||||||
|
return datagram;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Set payload’s size in a datagram.
|
||||||
|
* @param size integer representing payload size
|
||||||
|
* @param datagram datagram to be completed
|
||||||
|
* @throws InternalError
|
||||||
|
*/
|
||||||
|
protected static void setPayloadSize(int size, byte[] datagram) throws InternalError {
|
||||||
|
/* assert to help debugging */
|
||||||
|
assert size >= 0: "Payload size cannot be negative";
|
||||||
|
if (size < 0) {
|
||||||
|
// We don't throw SizeError
|
||||||
|
// because this is only for reception side
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
BytesArrayTools.write(datagram, PAYLOAD_SIZE_POSITION, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get payload’s size from a datagram.
|
||||||
|
* @param datagram the full datagram received
|
||||||
|
* @return integer representing payload size
|
||||||
|
* @throws SizeError
|
||||||
|
*/
|
||||||
|
protected static int getPayloadSize(byte[] datagram) throws SizeError {
|
||||||
|
return BytesArrayTools.readInt(datagram, PAYLOAD_SIZE_POSITION);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** RRCode getter.
|
||||||
|
* @return Request/Response code
|
||||||
|
*/
|
||||||
|
public RequestResponseCode getRequestResponseCode() {
|
||||||
|
return requestResponseCode;
|
||||||
|
}
|
||||||
|
}
|
243
src/protocolP2P/ProtocolP2PDatagram.java
Normal file
243
src/protocolP2P/ProtocolP2PDatagram.java
Normal file
@ -0,0 +1,243 @@
|
|||||||
|
package protocolP2P;
|
||||||
|
import exception.InternalError;
|
||||||
|
import exception.ProtocolError;
|
||||||
|
import exception.SizeError;
|
||||||
|
import exception.TransmissionError;
|
||||||
|
import exception.VersionError;
|
||||||
|
import remoteException.EmptyDirectory;
|
||||||
|
import remoteException.InternalRemoteError;
|
||||||
|
import remoteException.NotFound;
|
||||||
|
import remoteException.ProtocolRemoteError;
|
||||||
|
import remoteException.VersionRemoteError;
|
||||||
|
import remoteException.EmptyFile;
|
||||||
|
import protocolP2P.Payload;
|
||||||
|
import protocolP2P.RequestResponseCode;
|
||||||
|
import protocolP2P.LoadRequest;
|
||||||
|
import protocolP2P.FileList;
|
||||||
|
import protocolP2P.FilePart;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.lang.Byte;
|
||||||
|
import java.net.DatagramPacket;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.UnknownHostException;
|
||||||
|
|
||||||
|
/** Representation of datagram.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public class ProtocolP2PDatagram {
|
||||||
|
private final static byte PROTOCOL_VERSION = 0x11;
|
||||||
|
private final static int VERSION_POSITON = 0;
|
||||||
|
private byte version;
|
||||||
|
private Payload payload;
|
||||||
|
private InetAddress hostR;
|
||||||
|
private int portR;
|
||||||
|
|
||||||
|
/** Constructor with payload parameter (typically used when sending datagram).
|
||||||
|
* @param payload the payload associated with the datagram to send
|
||||||
|
*/
|
||||||
|
public ProtocolP2PDatagram(Payload payload) {
|
||||||
|
version = PROTOCOL_VERSION;
|
||||||
|
this.payload = payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send datagram on socket (from client)
|
||||||
|
* @param socket DatagramSocket used to send datagram.
|
||||||
|
* @param host host to send datagram (null if this is a response)
|
||||||
|
* @param port port to send datagram (null if this is a response)
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws UnknownHostException
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void send(DatagramSocket socket, String host, int port) throws InternalError, UnknownHostException, IOException {
|
||||||
|
InetAddress dst = InetAddress.getByName(host);
|
||||||
|
byte[] datagram = toDatagram();
|
||||||
|
// generate DatagramPacket
|
||||||
|
DatagramPacket datagramPacket = new DatagramPacket(datagram, datagram.length, dst, port);
|
||||||
|
// send it
|
||||||
|
socket.send(datagramPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send datagram on socket (from server, as a response)
|
||||||
|
* @param socket DatagramSocket used to send datagram.
|
||||||
|
* @param received datagram to respond (aka request)
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
public void send(DatagramSocket socket, ProtocolP2PDatagram received) throws InternalError, IOException {
|
||||||
|
assert received.getPortR() != 0 && received.getHostR() != null : "This method should be used only as response to a request";
|
||||||
|
if (received.getPortR() == 0 || received.getHostR() == null) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
byte[] datagram = toDatagram();
|
||||||
|
// generate DatagramPacket
|
||||||
|
DatagramPacket datagramPacket = new DatagramPacket(datagram, datagram.length, received.getHostR(), received.getPortR());
|
||||||
|
socket.send(datagramPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send a response.
|
||||||
|
* @param socket DatagramSocket used to send response
|
||||||
|
* @param host host to send response
|
||||||
|
* @param port port to send response
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws IOException
|
||||||
|
*/
|
||||||
|
protected void sendResponse(DatagramSocket socket, InetAddress host, int port) throws InternalError, IOException {
|
||||||
|
assert port != 0 && host != null : "This method should be used only as response to a request";
|
||||||
|
if (port == 0 || host == null) {
|
||||||
|
throw new InternalError();
|
||||||
|
}
|
||||||
|
byte[] datagram = toDatagram();
|
||||||
|
DatagramPacket datagramPacket = new DatagramPacket(datagram, datagram.length, host, port);
|
||||||
|
socket.send(datagramPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Receive datagram on socket
|
||||||
|
* @param socket DatagramSocket used to receive datagram
|
||||||
|
* @throws TransmissionError
|
||||||
|
* @throws ProtocolError
|
||||||
|
* @throws VersionError
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws SizeError
|
||||||
|
* @throws ProtocolRemoteError
|
||||||
|
* @throws VersionRemoteError
|
||||||
|
* @throws InternalRemoteError
|
||||||
|
* @throws EmptyDirectory
|
||||||
|
* @throws NotFound
|
||||||
|
* @throws IOException
|
||||||
|
* @throws EmptyFile
|
||||||
|
*/
|
||||||
|
public static ProtocolP2PDatagram receive(DatagramSocket socket) throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException {
|
||||||
|
// reception
|
||||||
|
byte[] datagram = new byte[4096];
|
||||||
|
DatagramPacket reception = new DatagramPacket(datagram, datagram.length);
|
||||||
|
socket.receive(reception);
|
||||||
|
// contruction
|
||||||
|
try {
|
||||||
|
ProtocolP2PDatagram p = new ProtocolP2PDatagram(datagram);
|
||||||
|
p.setHostR(reception.getAddress());
|
||||||
|
p.setPortR(reception.getPort());
|
||||||
|
Payload payload = p.getPayload();
|
||||||
|
switch (payload.getRequestResponseCode()) {
|
||||||
|
case PROTOCOL_ERROR :
|
||||||
|
throw new ProtocolRemoteError();
|
||||||
|
case VERSION_ERROR :
|
||||||
|
throw new VersionRemoteError();
|
||||||
|
case INTERNAL_ERROR :
|
||||||
|
throw new InternalRemoteError();
|
||||||
|
case EMPTY_DIRECTORY :
|
||||||
|
throw new EmptyDirectory();
|
||||||
|
case NOT_FOUND :
|
||||||
|
throw new NotFound();
|
||||||
|
case EMPTY_FILE:
|
||||||
|
throw new EmptyFile();
|
||||||
|
default :
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
} catch (TransmissionError e) {
|
||||||
|
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.INTERNAL_ERROR))).sendResponse(socket, reception.getAddress(), reception.getPort());
|
||||||
|
throw e;
|
||||||
|
} catch (ProtocolError e) {
|
||||||
|
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.PROTOCOL_ERROR))).sendResponse(socket, reception.getAddress(), reception.getPort());
|
||||||
|
throw e;
|
||||||
|
} catch (VersionError e) {
|
||||||
|
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.VERSION_ERROR))).sendResponse(socket, reception.getAddress(), reception.getPort());
|
||||||
|
throw e;
|
||||||
|
} catch (InternalError e) {
|
||||||
|
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.INTERNAL_ERROR))).sendResponse(socket, reception.getAddress(), reception.getPort());
|
||||||
|
throw e;
|
||||||
|
} catch (SizeError e) {
|
||||||
|
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.INTERNAL_ERROR))).sendResponse(socket, reception.getAddress(), reception.getPort());
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Private constructor with datagram as byte[] parameter (typically used when receiving datagram).
|
||||||
|
* @param datagram the full datagram received
|
||||||
|
* @throws TransmissionError
|
||||||
|
* @throws ProtocolError
|
||||||
|
* @throws VersionError
|
||||||
|
* @throws InternalError
|
||||||
|
* @throws SizeError
|
||||||
|
*/
|
||||||
|
private ProtocolP2PDatagram(byte[] datagram) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError {
|
||||||
|
// unwrap version
|
||||||
|
version = datagram[VERSION_POSITON];
|
||||||
|
checkProtocolVersion(); // this can throw VersionError
|
||||||
|
RequestResponseCode r = RequestResponseCode.fromCode(datagram[RequestResponseCode.RRCODE_POSITION]); // this can throw ProtocolError
|
||||||
|
switch (r) {
|
||||||
|
case LIST_RESPONSE:
|
||||||
|
payload = (Payload) new FileList(datagram);
|
||||||
|
break;
|
||||||
|
case LOAD_RESPONSE:
|
||||||
|
payload = (Payload) new FilePart(datagram);
|
||||||
|
break;
|
||||||
|
case LOAD_REQUEST:
|
||||||
|
payload = (Payload) new LoadRequest(datagram);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
payload = new Payload(datagram); // this can throw TransmissionError
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns a byte[] containing full datagram (typically used when sending datagram).
|
||||||
|
* This datagram is still complete and ready to be send.
|
||||||
|
* @return the full datagram to send
|
||||||
|
* @throws InternalError
|
||||||
|
*/
|
||||||
|
protected byte[] toDatagram() throws InternalError {
|
||||||
|
byte[] datagram = payload.toDatagram();
|
||||||
|
datagram[VERSION_POSITON] = version;
|
||||||
|
return datagram;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns Payload associated with the datagram.
|
||||||
|
* @return payload associated with the datagram
|
||||||
|
*/
|
||||||
|
public Payload getPayload() {
|
||||||
|
return payload;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Used to check protocol version when a datagram is constructed from bytes[].
|
||||||
|
* @throws VersionError
|
||||||
|
*/
|
||||||
|
private void checkProtocolVersion() throws VersionError {
|
||||||
|
if (PROTOCOL_VERSION != version) {
|
||||||
|
throw new VersionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** portR getter.
|
||||||
|
* @return portR
|
||||||
|
*/
|
||||||
|
protected int getPortR() {
|
||||||
|
return portR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** portR setter.
|
||||||
|
* @param portR portR
|
||||||
|
*/
|
||||||
|
protected void setPortR(int portR) {
|
||||||
|
this.portR = portR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** hostR getter.
|
||||||
|
* @return hostR
|
||||||
|
*/
|
||||||
|
protected InetAddress getHostR() {
|
||||||
|
return hostR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** hostH setter.
|
||||||
|
* @param hostR hostR
|
||||||
|
*/
|
||||||
|
protected void setHostR(InetAddress hostR) {
|
||||||
|
this.hostR = hostR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
65
src/protocolP2P/RequestResponseCode.java
Normal file
65
src/protocolP2P/RequestResponseCode.java
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package protocolP2P;
|
||||||
|
import protocolP2P.CodeType;
|
||||||
|
import exception.ProtocolError;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.lang.Byte;
|
||||||
|
|
||||||
|
/** Request/Response code enum.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public enum RequestResponseCode {
|
||||||
|
LIST_REQUEST(CodeType.REQUEST, (byte)0x00),
|
||||||
|
LOAD_REQUEST(CodeType.REQUEST, (byte)0x01),
|
||||||
|
LIST_RESPONSE(CodeType.RESPONSE, (byte)0x80),
|
||||||
|
LOAD_RESPONSE(CodeType.RESPONSE, (byte)0x81),
|
||||||
|
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);
|
||||||
|
|
||||||
|
public final CodeType codeType;
|
||||||
|
public final byte codeValue;
|
||||||
|
protected final static int RRCODE_POSITION = 1;
|
||||||
|
|
||||||
|
/* To be able to convert code to enum */
|
||||||
|
private static final Map<Byte, RequestResponseCode> BY_CODE = new HashMap<>();
|
||||||
|
/* Initialization of HashMap */
|
||||||
|
static {
|
||||||
|
for (RequestResponseCode r: values()) {
|
||||||
|
assert !BY_CODE.containsKey(Byte.valueOf(r.codeValue)) : "Duplicate in " + RequestResponseCode.class.getCanonicalName();
|
||||||
|
BY_CODE.put(Byte.valueOf(r.codeValue), r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Private constructor
|
||||||
|
* @param codeType type of code (request or response)
|
||||||
|
* @param codeValue value of the element in datagram
|
||||||
|
* @return enum element
|
||||||
|
*/
|
||||||
|
private RequestResponseCode(CodeType codeType, byte codeValue) {
|
||||||
|
this.codeType = codeType;
|
||||||
|
this.codeValue = codeValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gives enum from datagram code.
|
||||||
|
* @param code value of the element in datagram
|
||||||
|
* @return enum element
|
||||||
|
*/
|
||||||
|
protected static RequestResponseCode fromCode(byte code) throws ProtocolError {
|
||||||
|
RequestResponseCode r= BY_CODE.get(Byte.valueOf(code));
|
||||||
|
if (r == null) {
|
||||||
|
throw new ProtocolError();
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
4
src/remoteException/EmptyDirectory.java
Normal file
4
src/remoteException/EmptyDirectory.java
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package remoteException;
|
||||||
|
public class EmptyDirectory extends Exception {
|
||||||
|
private static final long serialVersionUID = 11L;
|
||||||
|
}
|
4
src/remoteException/EmptyFile.java
Normal file
4
src/remoteException/EmptyFile.java
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package remoteException;
|
||||||
|
public class EmptyFile extends Exception {
|
||||||
|
private static final long serialVersionUID = 11L;
|
||||||
|
}
|
4
src/remoteException/InternalRemoteError.java
Normal file
4
src/remoteException/InternalRemoteError.java
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package remoteException;
|
||||||
|
public class InternalRemoteError extends Exception {
|
||||||
|
private static final long serialVersionUID = 11L;
|
||||||
|
}
|
4
src/remoteException/NotFound.java
Normal file
4
src/remoteException/NotFound.java
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package remoteException;
|
||||||
|
public class NotFound extends Exception {
|
||||||
|
private static final long serialVersionUID = 11L;
|
||||||
|
}
|
4
src/remoteException/ProtocolRemoteError.java
Normal file
4
src/remoteException/ProtocolRemoteError.java
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package remoteException;
|
||||||
|
public class ProtocolRemoteError extends Exception {
|
||||||
|
private static final long serialVersionUID = 11L;
|
||||||
|
}
|
4
src/remoteException/VersionRemoteError.java
Normal file
4
src/remoteException/VersionRemoteError.java
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
package remoteException;
|
||||||
|
public class VersionRemoteError extends Exception {
|
||||||
|
private static final long serialVersionUID = 11L;
|
||||||
|
}
|
160
src/serverP2P/ServerManagementUDP.java
Normal file
160
src/serverP2P/ServerManagementUDP.java
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
package serverP2P;
|
||||||
|
import java.util.Vector;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.DatagramSocket;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import protocolP2P.ProtocolP2PDatagram;
|
||||||
|
import protocolP2P.RequestResponseCode;
|
||||||
|
import protocolP2P.Payload;
|
||||||
|
import protocolP2P.LoadRequest;
|
||||||
|
import protocolP2P.FileList;
|
||||||
|
import protocolP2P.FilePart;
|
||||||
|
import exception.InternalError;
|
||||||
|
import exception.ProtocolError;
|
||||||
|
import exception.SizeError;
|
||||||
|
import exception.TransmissionError;
|
||||||
|
import exception.VersionError;
|
||||||
|
import remoteException.EmptyDirectory;
|
||||||
|
import remoteException.InternalRemoteError;
|
||||||
|
import remoteException.NotFound;
|
||||||
|
import remoteException.ProtocolRemoteError;
|
||||||
|
import remoteException.VersionRemoteError;
|
||||||
|
import remoteException.EmptyFile;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
|
||||||
|
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for UDP.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public class ServerManagementUDP implements Runnable {
|
||||||
|
|
||||||
|
private String[] fileList;
|
||||||
|
private String baseDirectory;
|
||||||
|
private int UDPPort;
|
||||||
|
private DatagramSocket socket;
|
||||||
|
|
||||||
|
/** Constructor for UDP implementation, with baseDirectory and UDPPort parameters.
|
||||||
|
* @param baseDirectory the root directory where files are stored
|
||||||
|
* @param UDPPort the server will listen on this port
|
||||||
|
*/
|
||||||
|
public ServerManagementUDP(String baseDirectory, int UDPPort) {
|
||||||
|
this.baseDirectory = baseDirectory;
|
||||||
|
this.UDPPort = UDPPort;
|
||||||
|
initFileList();
|
||||||
|
try {
|
||||||
|
socket = new DatagramSocket(UDPPort);
|
||||||
|
} catch (SocketException e) {
|
||||||
|
System.err.println("Error: cannot listen on port " + UDPPort);
|
||||||
|
System.exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Implementation of runnable. This methods allows to run the server.
|
||||||
|
*/
|
||||||
|
public void run() {
|
||||||
|
while(true) {
|
||||||
|
try {
|
||||||
|
ProtocolP2PDatagram pd = ProtocolP2PDatagram.receive(socket);
|
||||||
|
Payload p = pd.getPayload();
|
||||||
|
switch (p.getRequestResponseCode()) {
|
||||||
|
case LOAD_REQUEST:
|
||||||
|
System.out.println("Received LOAD_REQUEST");
|
||||||
|
assert p instanceof LoadRequest : "payload must be an instance of LoadRequest";
|
||||||
|
if (!(p instanceof LoadRequest)) {
|
||||||
|
sendInternalError(pd);
|
||||||
|
} else {
|
||||||
|
String filename = ((LoadRequest)p).getFilename();
|
||||||
|
try {
|
||||||
|
byte[] load = Files.readAllBytes(Paths.get(baseDirectory + filename));
|
||||||
|
if (Arrays.binarySearch(fileList, filename) >= 0) {
|
||||||
|
try {
|
||||||
|
if (load.length == 0) {
|
||||||
|
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.EMPTY_FILE))).send(socket, pd);
|
||||||
|
} else {
|
||||||
|
(new ProtocolP2PDatagram((Payload)(new FilePart(filename, load.length, 0, load)))).send(socket, pd);
|
||||||
|
}
|
||||||
|
} catch (Exception e2) {
|
||||||
|
System.err.println(e2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new IOException(); // to send a NOT_FOUND in the catch block
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
try {
|
||||||
|
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.NOT_FOUND))).send(socket, pd);
|
||||||
|
} catch (Exception e2) {
|
||||||
|
System.err.println(e2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LIST_REQUEST:
|
||||||
|
System.out.println("Received LIST_REQUEST");
|
||||||
|
try {
|
||||||
|
if (fileList.length == 0) {
|
||||||
|
System.err.println("Sending EMPTY_DIRECTORY");
|
||||||
|
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.EMPTY_DIRECTORY))).send(socket, pd);
|
||||||
|
} else {
|
||||||
|
System.out.println("Sending LIST_RESPONSE");
|
||||||
|
(new ProtocolP2PDatagram((Payload)(new FileList(fileList)))).send(socket, pd);
|
||||||
|
}
|
||||||
|
} catch (Exception e2) {
|
||||||
|
System.err.println(e2);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sendInternalError(pd);
|
||||||
|
}
|
||||||
|
} catch (NotFound e) {
|
||||||
|
} catch (EmptyDirectory e) {
|
||||||
|
} catch (InternalRemoteError e) {
|
||||||
|
} catch (VersionRemoteError e) {
|
||||||
|
} catch (ProtocolRemoteError e) {
|
||||||
|
} catch (IOException e) {
|
||||||
|
} catch (TransmissionError e) {
|
||||||
|
} catch (ProtocolError e) {
|
||||||
|
} catch (VersionError e) {
|
||||||
|
} catch (InternalError e) {
|
||||||
|
} catch (SizeError e) {
|
||||||
|
} catch (EmptyFile e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Initialize local list of all files allowed to be shared.
|
||||||
|
*/
|
||||||
|
private void initFileList() {
|
||||||
|
File folder = new File(baseDirectory);
|
||||||
|
Vector<String> v = new Vector<String>();
|
||||||
|
File[] files = folder.listFiles();
|
||||||
|
/* Add non-recursively files's names to fileList */
|
||||||
|
for (File f : files) {
|
||||||
|
if (f.isFile()) {
|
||||||
|
v.add(f.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fileList = new String[v.size()];
|
||||||
|
v.toArray(fileList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Send an internal error message.
|
||||||
|
* @param pd ProtocolP2PDatagram to respond
|
||||||
|
*/
|
||||||
|
private void sendInternalError(ProtocolP2PDatagram pd) {
|
||||||
|
try {
|
||||||
|
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(socket, pd);
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
23
src/serverP2P/ServerP2P.java
Normal file
23
src/serverP2P/ServerP2P.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package serverP2P;
|
||||||
|
import serverP2P.ServerManagementUDP;
|
||||||
|
import tools.Directories;
|
||||||
|
|
||||||
|
public class ServerP2P {
|
||||||
|
private int port;
|
||||||
|
private Directories directories;
|
||||||
|
|
||||||
|
public ServerP2P() {
|
||||||
|
directories = new Directories("P2P_JAVA_PROJECT_SERVER");
|
||||||
|
port = 40001;
|
||||||
|
System.out.println("Server will listen on port " + port + " and serve files from " + directories.getDataHomeDirectory());
|
||||||
|
directories.askOpenDataHomeDirectory();
|
||||||
|
}
|
||||||
|
public static void main(String [] args) {
|
||||||
|
ServerP2P s = new ServerP2P();
|
||||||
|
ServerManagementUDP sm = new ServerManagementUDP(s.directories.getDataHomeDirectory(), s.port);
|
||||||
|
Thread t = new Thread(sm);
|
||||||
|
t.setName("server P2P-JAVA-PROJECT");
|
||||||
|
t.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
66
src/tools/BytesArrayTools.java
Normal file
66
src/tools/BytesArrayTools.java
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
package tools;
|
||||||
|
import exception.SizeError;
|
||||||
|
/** Helper to manipulate byte[].
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public class BytesArrayTools {
|
||||||
|
/** Write int in a bytearray
|
||||||
|
* @param array the array to write
|
||||||
|
* @param start where to begin writting
|
||||||
|
* @param value int to write
|
||||||
|
*/
|
||||||
|
public static void write(byte[] array, int start, int value) {
|
||||||
|
for(int i=0;i<4;i++) {
|
||||||
|
array[start + i] = (byte) ((value >> (8 * (3 - i))) & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Write long in a bytearray
|
||||||
|
* @param array the array to write
|
||||||
|
* @param start where to begin writting
|
||||||
|
* @param value long to write
|
||||||
|
*/
|
||||||
|
public static void write(byte[] array, int start, long value) {
|
||||||
|
for(int i=0;i<8;i++) {
|
||||||
|
array[start + i] = (byte) ((value >> (8 * (7 - i))) & 0xFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Read int from a bytearray
|
||||||
|
* @param array the array to read
|
||||||
|
* @param start where to begin reading
|
||||||
|
* @return value read as int
|
||||||
|
*/
|
||||||
|
public static int readInt(byte[] array, int start) throws SizeError {
|
||||||
|
int size = 0;
|
||||||
|
for(int i=0;i<4;i++) {
|
||||||
|
size |= ((int)array[start + i]) << (8* (3 -i));
|
||||||
|
}
|
||||||
|
if (size < 0) {
|
||||||
|
// Size in array is probably correct
|
||||||
|
// but we cannot store it into a int (or this will be negative)
|
||||||
|
throw new SizeError();
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
/** Read long from a bytearray
|
||||||
|
* @param array the array to read
|
||||||
|
* @param start where to begin reading
|
||||||
|
* @return value read as long
|
||||||
|
*/
|
||||||
|
public static long readLong(byte[] array, int start) throws SizeError {
|
||||||
|
long size = 0;
|
||||||
|
for(int i=0;i<8;i++) {
|
||||||
|
size |= ((int)array[start + i]) << (8* (7 - i));
|
||||||
|
}
|
||||||
|
if (size < 0) {
|
||||||
|
// Size in array is probably correct
|
||||||
|
// but we cannot store it into a int (or this will be negative)
|
||||||
|
throw new SizeError();
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
89
src/tools/Directories.java
Normal file
89
src/tools/Directories.java
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package tools;
|
||||||
|
import java.util.Scanner;
|
||||||
|
import java.io.File;
|
||||||
|
import java.lang.Runtime;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
|
||||||
|
/** Helper to get application directories.
|
||||||
|
* @author Louis Royer
|
||||||
|
* @author Flavien Haas
|
||||||
|
* @author JS Auge
|
||||||
|
* @version 1.0
|
||||||
|
*/
|
||||||
|
public class Directories {
|
||||||
|
private String projectName;
|
||||||
|
private String dataHomeDirectory;
|
||||||
|
private String os;
|
||||||
|
|
||||||
|
/** Constructor with projectName parameter.
|
||||||
|
* @param projectName name of the project
|
||||||
|
*/
|
||||||
|
public Directories(String projectName) {
|
||||||
|
this.projectName = projectName;
|
||||||
|
os = System.getProperty("os.name");
|
||||||
|
setDataHomeDirectory();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Setter for dataHomeDirectory. Will create the directory if not already exists.
|
||||||
|
*/
|
||||||
|
private void setDataHomeDirectory() {
|
||||||
|
/* Follow XDG Base Directory Specification
|
||||||
|
* https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||||
|
*/
|
||||||
|
if (os.equals("Linux")) {
|
||||||
|
dataHomeDirectory = System.getenv().get("XDG_DATA_HOME");
|
||||||
|
if (dataHomeDirectory == null || dataHomeDirectory.equals("")) {
|
||||||
|
dataHomeDirectory = System.getProperty("user.home") + "/.local/share";
|
||||||
|
}
|
||||||
|
} else if (os.equals("Mac")||os.equals("Mac OS X")) {
|
||||||
|
/* Apple MacOS X User Data Directory
|
||||||
|
* https://developer.apple.com/library/archive/qa/qa1170/_index.html */
|
||||||
|
dataHomeDirectory = System.getProperty("user.home") + "/Library";
|
||||||
|
} else {
|
||||||
|
dataHomeDirectory = ".";
|
||||||
|
}
|
||||||
|
dataHomeDirectory += "/" + projectName + "/";
|
||||||
|
// create directory if not already exists
|
||||||
|
new File(dataHomeDirectory).mkdirs();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Getter for dataHomeDirectory.
|
||||||
|
* @return path to the application home directory
|
||||||
|
*/
|
||||||
|
public String getDataHomeDirectory() {
|
||||||
|
return dataHomeDirectory;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Opens dataHomeDirectory if supported.
|
||||||
|
*/
|
||||||
|
private void openDataHomeDirectory() {
|
||||||
|
try {
|
||||||
|
if (os.equals("Linux")) {
|
||||||
|
Runtime runtime = Runtime.getRuntime();
|
||||||
|
runtime.exec(new String[] { "xdg-open", dataHomeDirectory });
|
||||||
|
} else if (os.equals("Mac")||os.equals("Mac OS X")) {
|
||||||
|
Runtime runtime = Runtime.getRuntime();
|
||||||
|
runtime.exec(new String[] { "open", dataHomeDirectory });
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.out.println("Error encountered while trying to open directory");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Asks the user to choose opening dataHomeDirectory or not.
|
||||||
|
*/
|
||||||
|
public void askOpenDataHomeDirectory() {
|
||||||
|
if (os.equals("Linux") || os.equals("Mac") || os.equals("Mac OS X")) {
|
||||||
|
System.out.println("Do you want to open this directory? (y/N)");
|
||||||
|
Scanner scanner = new Scanner(System.in);
|
||||||
|
String resp = scanner.nextLine();
|
||||||
|
if (resp.equals("y") || resp.equals("Y")) {
|
||||||
|
System.out.println("Openning");
|
||||||
|
openDataHomeDirectory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user