Implement ServerManagement and ClientManagement
This commit is contained in:
parent
4fec744616
commit
2d20357150
@ -1,18 +1,30 @@
|
||||
package clientP2P;
|
||||
import exception.NotFound;
|
||||
import exception.ProtocolError;
|
||||
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 java.net.UnknownHostException;
|
||||
import java.util.Scanner;
|
||||
import java.net.InetAddress;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import java.io.File;
|
||||
import java.net.SocketException;
|
||||
import java.io.IOException;
|
||||
import java.io.FileWriter;
|
||||
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 VERSION 1.0 protocol for UDP.
|
||||
/** Implementation of P2P-JAVA-PROJECT CLIENT
|
||||
* @author Louis Royer
|
||||
* @author Flavien Haas
|
||||
* @author JS Auge
|
||||
@ -22,142 +34,139 @@ public class ClientManagementUDP implements Runnable {
|
||||
private String baseDirectory;
|
||||
private int UDPPort;
|
||||
private String host;
|
||||
private final String protocolID = "P2P-JAVA-PROJECT VERSION 1.0";
|
||||
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() {
|
||||
System.out.println("Files present on the server:");
|
||||
try {
|
||||
String msg = sendMsg(sendListDirectoryRequest());
|
||||
System.out.println(listDirectory(msg));
|
||||
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();
|
||||
msg = sendMsg(sendDownloadRequest(f));
|
||||
download(msg, f);
|
||||
} catch (TransmissionError e) {
|
||||
System.out.println("Transmission error");
|
||||
} catch (ProtocolError e) {
|
||||
System.out.println("Protocol error");
|
||||
} catch (NotFound e) {
|
||||
System.out.println("File not found");
|
||||
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.out.println("Server internal error");
|
||||
System.err.println("Error: Client internal error");
|
||||
} catch (UnknownHostException e) {
|
||||
System.err.println("Error: Server host is unknown");
|
||||
} catch (IOException e) {
|
||||
System.out.println("Cannot write to file");
|
||||
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 have not this file in directory");
|
||||
}
|
||||
}
|
||||
|
||||
/** Prepare request to download file
|
||||
* @param filename name of the file to be downloaded
|
||||
* @return request to be send
|
||||
*/
|
||||
private String sendDownloadRequest(String filename) {
|
||||
return protocolID + "\nDOWNLOAD\n" + filename + "\n";
|
||||
}
|
||||
|
||||
/** Download file.
|
||||
* @param response Servers's response
|
||||
* @throws NotFound
|
||||
* @throws ProtocolError
|
||||
* @throws InternalError
|
||||
*/
|
||||
private void download(String response, String filename) throws TransmissionError, NotFound, ProtocolError, InternalError, IOException {
|
||||
try {
|
||||
String r[] = response.split("\n");
|
||||
checkProtocolID(r[0]);
|
||||
switch (r[1]) {
|
||||
case "LOAD":
|
||||
int size = Integer.parseInt(r[2]);
|
||||
if (r[3].length() != size - 1) {
|
||||
throw new TransmissionError();
|
||||
}
|
||||
FileWriter fileWriter = new FileWriter(baseDirectory + filename);
|
||||
fileWriter.write(r[3]);
|
||||
fileWriter.close();
|
||||
break;
|
||||
case "NOT FOUND":
|
||||
throw new NotFound();
|
||||
case "INTERNAL ERROR":
|
||||
throw new InternalError();
|
||||
default:
|
||||
System.out.println("Error: Unknow format `" + r[1] + "`");
|
||||
throw new ProtocolError();
|
||||
}
|
||||
} catch (java.lang.ArrayIndexOutOfBoundsException e) {
|
||||
System.out.println("Error: IndexOutOfBonds");
|
||||
throw new ProtocolError();
|
||||
} catch (NumberFormatException e) {
|
||||
System.out.println("Error: NumberFormat");
|
||||
throw new ProtocolError();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prepare request to list files on server
|
||||
* @return request to be send
|
||||
*/
|
||||
private String sendListDirectoryRequest() {
|
||||
return protocolID + "\nLIST\n";
|
||||
}
|
||||
|
||||
/** Parse list of directory response from server
|
||||
* @param response server's response
|
||||
* @return list of files, separated by CRLF
|
||||
* @throws ProtocolError
|
||||
*/
|
||||
private String listDirectory(String response) throws ProtocolError {
|
||||
try {
|
||||
String r[] = response.split("\n");
|
||||
checkProtocolID(r[0]);
|
||||
return response.split(protocolID + "\nLIST\n")[1].split("\n\n")[0];
|
||||
} catch (java.lang.ArrayIndexOutOfBoundsException e) {
|
||||
throw new ProtocolError();
|
||||
}
|
||||
}
|
||||
|
||||
/** Check client's protocol identifier matches message's protocol identifier.
|
||||
* Throws a ProtocolError if mismatched.
|
||||
* @param msgProtocolID part of the request containing protocol identifier
|
||||
/** 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
|
||||
*/
|
||||
private void checkProtocolID(String msgProtocolID) throws ProtocolError {
|
||||
if (!protocolID.equals(msgProtocolID)) {
|
||||
throw new ProtocolError();
|
||||
}
|
||||
private void download(String filename) throws 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("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();
|
||||
}
|
||||
|
||||
/** Send message to server
|
||||
* @param msg message to be send
|
||||
* @return server's response
|
||||
*/
|
||||
private String sendMsg(String msg) throws TransmissionError {
|
||||
//TODO changer le gros try catch
|
||||
try{
|
||||
InetAddress dst = InetAddress.getByName(host);
|
||||
byte [] buffer = msg.getBytes();
|
||||
byte [] buffer2 = new byte[1500];
|
||||
DatagramSocket socket = new DatagramSocket();
|
||||
DatagramPacket reception = new DatagramPacket(buffer2, 1500);
|
||||
DatagramPacket emission = new DatagramPacket(buffer, buffer.length, dst, UDPPort);
|
||||
socket.send(emission);
|
||||
socket.receive(reception);
|
||||
return new String(reception.getData(), 0, reception.getLength());
|
||||
} catch (Exception e){
|
||||
System.out.println(e);
|
||||
throw new TransmissionError();
|
||||
}
|
||||
}
|
||||
|
||||
/** 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ public class ClientP2P {
|
||||
public ClientP2P() {
|
||||
directories = new Directories("P2P_JAVA_PROJECT_CLIENT");
|
||||
host = "localhost";
|
||||
port = 40000;
|
||||
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();
|
||||
}
|
||||
|
@ -15,21 +15,21 @@ import java.io.UnsupportedEncodingException;
|
||||
* @version 1.0
|
||||
*/
|
||||
public class FileList extends Payload {
|
||||
private String[] content;
|
||||
private String[] fileList;
|
||||
|
||||
/** Constructor (typically used by the server) with an ArrayList parameter containing
|
||||
* filenames.
|
||||
* @param content a list of files. Must not be empty.
|
||||
* @param fileList a list of files. Must not be empty.
|
||||
* @throws InternalError
|
||||
*/
|
||||
public FileList(String[] content) throws InternalError {
|
||||
public FileList(String[] fileList) throws InternalError {
|
||||
super(RequestResponseCode.LIST_RESPONSE);
|
||||
/* assert to help debugging */
|
||||
assert content.length != 0 : "Payload size of FileList must not be empty, use EmptyDirectory from Payload instead";
|
||||
if (content.length == 0) {
|
||||
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.content = content;
|
||||
this.fileList = fileList;
|
||||
}
|
||||
|
||||
/** Constructor (typically used by client) with a byte[] parameter containing the datagram received.
|
||||
@ -49,7 +49,7 @@ public class FileList extends Payload {
|
||||
}
|
||||
int size = getPayloadSize(datagram);
|
||||
try {
|
||||
content = (new String(datagram, 8, size, "UTF-8")).split("\n");
|
||||
fileList = (new String(datagram, 8, size, "UTF-8")).split("\n");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new InternalError();
|
||||
}
|
||||
@ -64,18 +64,17 @@ public class FileList extends Payload {
|
||||
protected byte[] toDatagram() throws InternalError {
|
||||
// compute size
|
||||
int size = 8;
|
||||
for (String s : content) {
|
||||
for (String s : fileList) {
|
||||
size += s.length();
|
||||
}
|
||||
byte[] datagram = new byte[size]; // java initialize all to zero
|
||||
// size is keep blank (ProtocolP2PDatagram will handle it)
|
||||
// set request/response code
|
||||
datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||||
// bits 16-31 are reserved for future use
|
||||
setPayloadSize(size, datagram);
|
||||
// Write content
|
||||
setPayloadSize(size - 8, datagram);
|
||||
// Write fileList
|
||||
int bCount = 8;
|
||||
for(String s : content) {
|
||||
for(String s : fileList) {
|
||||
if (bCount != 8) { // not on first iteration
|
||||
try {
|
||||
datagram[bCount] = "\n".getBytes("UTF-8")[0]; // separator
|
||||
@ -98,4 +97,11 @@ public class FileList extends Payload {
|
||||
}
|
||||
return datagram;
|
||||
}
|
||||
|
||||
/** fileList getter.
|
||||
* @return fileList
|
||||
*/
|
||||
public String[] getFileList() {
|
||||
return fileList;
|
||||
}
|
||||
}
|
||||
|
@ -76,11 +76,10 @@ public class FilePart extends Payload {
|
||||
// compute payload size
|
||||
int size = 28 + filename.length() + partialContent.length;
|
||||
byte[] datagram = new byte[size]; // java initialize all to zero
|
||||
// size is keep blank (ProtocolP2PDatagram will handle it)
|
||||
// set request/response code
|
||||
datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
|
||||
// bits 16-31 are reserved for future use
|
||||
setPayloadSize(size, datagram);
|
||||
setPayloadSize(size - 8, datagram);
|
||||
// write offset to datagram (Byte 8)
|
||||
BytesArrayTools.write(datagram, 8, offset);
|
||||
// write totalSize to datagram (Byte 16)
|
||||
@ -90,20 +89,20 @@ public class FilePart extends Payload {
|
||||
// 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();
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/** Write from bytes 8 to 15 of datagram into offset.
|
||||
@ -170,4 +169,31 @@ public class FilePart extends Payload {
|
||||
}
|
||||
}
|
||||
|
||||
/** 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;
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package protocolP2P;
|
||||
import protocolP2P.RequestResponseCode;
|
||||
import protocolP2P.FilePart;
|
||||
import protocolP2P.FileList;
|
||||
import protocolP2P.LoadRequest;
|
||||
import exception.ProtocolError;
|
||||
import exception.InternalError;
|
||||
import exception.TransmissionError;
|
||||
@ -26,6 +27,7 @@ public class Payload {
|
||||
/* 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
|
||||
}
|
||||
@ -46,6 +48,7 @@ public class Payload {
|
||||
}
|
||||
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
|
||||
}
|
||||
@ -56,7 +59,8 @@ public class Payload {
|
||||
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_RESPONSE && !(this instanceof FilePart))
|
||||
|| (requestResponseCode == RequestResponseCode.LOAD_REQUEST && !(this instanceof LoadRequest))) {
|
||||
throw new InternalError();
|
||||
}
|
||||
}
|
||||
@ -70,7 +74,7 @@ public class Payload {
|
||||
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 keep blank (ProtocolP2PDatagram will handle it)
|
||||
// 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
|
||||
@ -102,4 +106,11 @@ public class Payload {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,19 @@
|
||||
package protocolP2P;
|
||||
import exception.ProtocolError;
|
||||
import exception.VersionError;
|
||||
import exception.SizeError;
|
||||
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 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;
|
||||
@ -25,6 +33,8 @@ public class ProtocolP2PDatagram {
|
||||
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
|
||||
@ -53,32 +63,86 @@ public class ProtocolP2PDatagram {
|
||||
|
||||
/** 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) throws InternalError, 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);
|
||||
DatagramPacket datagramPacket = new DatagramPacket(datagram, datagram.length, received.getHostR(), received.getPortR());
|
||||
socket.send(datagramPacket);
|
||||
}
|
||||
|
||||
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
|
||||
* @return payload of the datagram
|
||||
* @throws TransmissionError
|
||||
* @throws ProtocolError
|
||||
* @throws VersionError
|
||||
* @throws InternalError
|
||||
* @throws SizeError
|
||||
* @throws ProtocolRemoteError
|
||||
* @throws VersionRemoteError
|
||||
* @throws InternalRemoteError
|
||||
* @throws EmptyDirectory
|
||||
* @throws NotFound
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Payload receive(DatagramSocket socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError {
|
||||
public static ProtocolP2PDatagram receive(DatagramSocket socket) throws 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
|
||||
ProtocolP2PDatagram p = new ProtocolP2PDatagram(datagram);
|
||||
return p.getPayload();
|
||||
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();
|
||||
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
|
||||
@ -100,6 +164,9 @@ public class ProtocolP2PDatagram {
|
||||
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;
|
||||
@ -111,7 +178,7 @@ public class ProtocolP2PDatagram {
|
||||
* @return the full datagram to send
|
||||
* @throws InternalError
|
||||
*/
|
||||
private byte[] toDatagram() throws InternalError {
|
||||
protected byte[] toDatagram() throws InternalError {
|
||||
byte[] datagram = payload.toDatagram();
|
||||
datagram[VERSION_POSITON] = version;
|
||||
return datagram;
|
||||
@ -121,7 +188,7 @@ public class ProtocolP2PDatagram {
|
||||
/** Returns Payload associated with the datagram.
|
||||
* @return payload associated with the datagram
|
||||
*/
|
||||
private Payload getPayload() {
|
||||
public Payload getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
@ -133,5 +200,33 @@ public class ProtocolP2PDatagram {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +50,7 @@ public enum RequestResponseCode {
|
||||
* @param code value of the element in datagram
|
||||
* @return enum element
|
||||
*/
|
||||
public static RequestResponseCode fromCode(byte code) throws ProtocolError {
|
||||
protected static RequestResponseCode fromCode(byte code) throws ProtocolError {
|
||||
RequestResponseCode r= BY_CODE.get(Byte.valueOf(code));
|
||||
if (r == null) {
|
||||
throw new ProtocolError();
|
||||
|
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/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;
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package exception;
|
||||
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;
|
||||
}
|
@ -2,13 +2,26 @@ package serverP2P;
|
||||
import java.util.Vector;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
import exception.ProtocolError;
|
||||
import exception.NotFound;
|
||||
import exception.InternalError;
|
||||
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;
|
||||
|
||||
|
||||
/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for UDP.
|
||||
@ -19,10 +32,10 @@ import java.nio.file.Files;
|
||||
*/
|
||||
public class ServerManagementUDP implements Runnable {
|
||||
|
||||
private Vector<String> fileList;
|
||||
private String[] fileList;
|
||||
private String baseDirectory;
|
||||
private int UDPPort;
|
||||
private final String protocolID = "P2P-JAVA-PROJECT VERSION 1.0";
|
||||
private DatagramSocket socket;
|
||||
|
||||
/** Constructor for UDP implementation, with baseDirectory and UDPPort parameters.
|
||||
* @param baseDirectory the root directory where files are stored
|
||||
@ -31,148 +44,106 @@ public class ServerManagementUDP implements Runnable {
|
||||
public ServerManagementUDP(String baseDirectory, int UDPPort) {
|
||||
this.baseDirectory = baseDirectory;
|
||||
this.UDPPort = UDPPort;
|
||||
fileList = new Vector<String>();
|
||||
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() {
|
||||
try {
|
||||
// socket creation on port UDPPort
|
||||
DatagramSocket socket = new DatagramSocket(UDPPort);
|
||||
// buffer to receive UDP Datagram
|
||||
final byte[] buffer = new byte[1024];
|
||||
while(true) {
|
||||
// java object to receive Datagram
|
||||
DatagramPacket dgram = new DatagramPacket(buffer, buffer.length);
|
||||
// wait and receive datagram
|
||||
socket.receive(dgram);
|
||||
// extract data
|
||||
String str = new String(dgram.getData(), 0, dgram.getLength());
|
||||
// process request
|
||||
str = processRequest(str);
|
||||
dgram.setData(str.getBytes());
|
||||
dgram.setLength(str.getBytes().length);
|
||||
// send response
|
||||
socket.send(dgram);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// TODO: treat exceptions
|
||||
}
|
||||
}
|
||||
|
||||
/** Process the request received.
|
||||
* @param request the request received
|
||||
* @return data to be send as response
|
||||
*/
|
||||
String processRequest(String request) {
|
||||
String res = protocolID + "\n";
|
||||
String formattedRequest[] = request.split("\n");
|
||||
try {
|
||||
while(true) {
|
||||
try {
|
||||
checkProtocolID(formattedRequest[0]);
|
||||
switch (formattedRequest[1]) {
|
||||
case "LIST":
|
||||
System.out.println("List request");
|
||||
res += sendFileList();
|
||||
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));
|
||||
try {
|
||||
(new ProtocolP2PDatagram((Payload)(new FilePart(filename, load.length, 0, load)))).send(socket, pd);
|
||||
} catch (Exception e2) {
|
||||
System.err.println(e2);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
try {
|
||||
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.NOT_FOUND))).send(socket, pd);
|
||||
} catch (Exception e2) {
|
||||
System.err.println(e2);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "DOWNLOAD":
|
||||
System.out.println("Download request: " + formattedRequest[2]);
|
||||
res += upload(formattedRequest[2]);
|
||||
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:
|
||||
throw new ProtocolError();
|
||||
sendInternalError(pd);
|
||||
}
|
||||
} catch (java.lang.ArrayIndexOutOfBoundsException e) {
|
||||
throw new ProtocolError();
|
||||
} 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 (ProtocolError e) {
|
||||
// wrong version or wrong implementation
|
||||
res += sendProtocolError();
|
||||
} catch (InternalError e) {
|
||||
res += sendInternalError();
|
||||
} catch (NotFound e) {
|
||||
res += sendNotFound();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** 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()) {
|
||||
fileList.add(f.getName());
|
||||
v.add(f.getName());
|
||||
}
|
||||
}
|
||||
fileList = new String[v.size()];
|
||||
v.toArray(fileList);
|
||||
}
|
||||
|
||||
/** Check server's protocol identifier matches message's protocol identifier.
|
||||
* Throws a ProtocolError if mismatched.
|
||||
* @param msgProtocolID part of the request containing protocol identifier
|
||||
* @throws ProtocolError
|
||||
*/
|
||||
private void checkProtocolID(String msgProtocolID) throws ProtocolError {
|
||||
if (!protocolID.equals(msgProtocolID)) {
|
||||
throw new ProtocolError();
|
||||
}
|
||||
}
|
||||
|
||||
/** Prepare the data to be send if a file is requested
|
||||
* @param filename name of the file to be send
|
||||
* @return data to be send
|
||||
* @throws NotFound
|
||||
* @throws InternalError
|
||||
|
||||
/** Send an internal error message.
|
||||
* @param pd ProtocolP2PDatagram to respond
|
||||
*/
|
||||
private String upload(String filename) throws NotFound, InternalError {
|
||||
File file = new File(baseDirectory + filename);
|
||||
System.out.println("Uploading `" + baseDirectory + filename + "`");
|
||||
if (!file.exists() || !file.isFile()) {
|
||||
throw new NotFound();
|
||||
}
|
||||
String res = "LOAD\n" + file.length() + "\n";
|
||||
private void sendInternalError(ProtocolP2PDatagram pd) {
|
||||
try {
|
||||
res += new String(Files.readAllBytes(Paths.get(baseDirectory + filename)));
|
||||
} catch (IOException e) {
|
||||
throw new InternalError();
|
||||
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(socket, pd);
|
||||
} catch (Exception e) {
|
||||
System.err.println(e);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/** Prepare the data to be send if file list is requested
|
||||
* @return data to be send
|
||||
*/
|
||||
private String sendFileList() {
|
||||
String res = "LIST\n";
|
||||
for (String f : fileList) {
|
||||
res += (f + "\n");
|
||||
}
|
||||
return res + "\n";
|
||||
}
|
||||
|
||||
/** Prepare data to be send if protocol error is detected
|
||||
* @return data to be send
|
||||
*/
|
||||
private String sendProtocolError() {
|
||||
return "PROTOCOL ERROR\n";
|
||||
}
|
||||
|
||||
/** Prepare data to be send if file is not found
|
||||
* @return data to be send
|
||||
*/
|
||||
private String sendNotFound() {
|
||||
return "NOT FOUND\n";
|
||||
}
|
||||
|
||||
/** Prepare data to be send if internal error encounterred
|
||||
* @return data to be send
|
||||
*/
|
||||
private String sendInternalError() {
|
||||
return "INTERNAL ERROR\n";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ public class ServerP2P {
|
||||
|
||||
public ServerP2P() {
|
||||
directories = new Directories("P2P_JAVA_PROJECT_SERVER");
|
||||
port = 40000;
|
||||
port = 40001;
|
||||
System.out.println("Server will listen on port " + port + " and serve files from " + directories.getDataHomeDirectory());
|
||||
directories.askOpenDataHomeDirectory();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user