From 2d20357150ed2713a45ffef45fc5dfea895cd315 Mon Sep 17 00:00:00 2001 From: Louis Date: Sat, 25 Jan 2020 17:48:21 +0100 Subject: [PATCH] Implement ServerManagement and ClientManagement --- src/clientP2P/ClientManagementUDP.java | 223 +++++++++--------- src/clientP2P/ClientP2P.java | 2 +- src/protocolP2P/FileList.java | 30 ++- src/protocolP2P/FilePart.java | 52 +++- src/protocolP2P/LoadRequest.java | 90 +++++++ src/protocolP2P/Payload.java | 15 +- src/protocolP2P/ProtocolP2PDatagram.java | 115 ++++++++- src/protocolP2P/RequestResponseCode.java | 2 +- src/remoteException/EmptyDirectory.java | 4 + src/remoteException/InternalRemoteError.java | 4 + .../NotFound.java | 2 +- src/remoteException/ProtocolRemoteError.java | 4 + src/remoteException/VersionRemoteError.java | 4 + src/serverP2P/ServerManagementUDP.java | 205 +++++++--------- src/serverP2P/ServerP2P.java | 2 +- 15 files changed, 489 insertions(+), 265 deletions(-) create mode 100644 src/protocolP2P/LoadRequest.java create mode 100644 src/remoteException/EmptyDirectory.java create mode 100644 src/remoteException/InternalRemoteError.java rename src/{exception => remoteException}/NotFound.java (79%) create mode 100644 src/remoteException/ProtocolRemoteError.java create mode 100644 src/remoteException/VersionRemoteError.java diff --git a/src/clientP2P/ClientManagementUDP.java b/src/clientP2P/ClientManagementUDP.java index a64aa48..3b8a811 100644 --- a/src/clientP2P/ClientManagementUDP.java +++ b/src/clientP2P/ClientManagementUDP.java @@ -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); + 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.out.println("Transmission error"); + System.err.println("Error: Message received is too big"); } catch (ProtocolError e) { - System.out.println("Protocol error"); + 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.out.println("File not found"); - } catch (InternalError e) { - System.out.println("Server internal error"); - } catch (IOException e) { - System.out.println("Cannot write to file"); + 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 + /** 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 download(String response, String filename) throws TransmissionError, NotFound, ProtocolError, InternalError, IOException { + 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 { - 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": + Payload p = ProtocolP2PDatagram.receive(socket).getPayload(); + assert p instanceof FilePart : "This payload must be instance of FilePart"; + if (!(p instanceof FilePart)) { throw new InternalError(); - default: - System.out.println("Error: Unknow format `" + r[1] + "`"); - throw new ProtocolError(); + } 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 (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"; + } catch (EmptyDirectory e) { + throw new ProtocolError(); } - /** 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 + /** 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 void checkProtocolID(String msgProtocolID) throws ProtocolError { - if (!protocolID.equals(msgProtocolID)) { + 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(); } } - - /** 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(); - } - - } } diff --git a/src/clientP2P/ClientP2P.java b/src/clientP2P/ClientP2P.java index 32c2641..8ee0186 100644 --- a/src/clientP2P/ClientP2P.java +++ b/src/clientP2P/ClientP2P.java @@ -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(); } diff --git a/src/protocolP2P/FileList.java b/src/protocolP2P/FileList.java index 6734f38..9f141c9 100644 --- a/src/protocolP2P/FileList.java +++ b/src/protocolP2P/FileList.java @@ -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; + } } diff --git a/src/protocolP2P/FilePart.java b/src/protocolP2P/FilePart.java index d4a3fb2..32deb5f 100644 --- a/src/protocolP2P/FilePart.java +++ b/src/protocolP2P/FilePart.java @@ -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; + } } diff --git a/src/protocolP2P/LoadRequest.java b/src/protocolP2P/LoadRequest.java new file mode 100644 index 0000000..af3082f --- /dev/null +++ b/src/protocolP2P/LoadRequest.java @@ -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; + } +} diff --git a/src/protocolP2P/Payload.java b/src/protocolP2P/Payload.java index f09010c..c3638f6 100644 --- a/src/protocolP2P/Payload.java +++ b/src/protocolP2P/Payload.java @@ -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; + } } diff --git a/src/protocolP2P/ProtocolP2PDatagram.java b/src/protocolP2P/ProtocolP2PDatagram.java index 730eac3..caa8874 100644 --- a/src/protocolP2P/ProtocolP2PDatagram.java +++ b/src/protocolP2P/ProtocolP2PDatagram.java @@ -1,11 +1,19 @@ package protocolP2P; +import exception.InternalError; import exception.ProtocolError; -import exception.VersionError; import exception.SizeError; -import exception.InternalError; 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; + } } diff --git a/src/protocolP2P/RequestResponseCode.java b/src/protocolP2P/RequestResponseCode.java index 81f3cad..e030bc1 100644 --- a/src/protocolP2P/RequestResponseCode.java +++ b/src/protocolP2P/RequestResponseCode.java @@ -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(); diff --git a/src/remoteException/EmptyDirectory.java b/src/remoteException/EmptyDirectory.java new file mode 100644 index 0000000..39146fd --- /dev/null +++ b/src/remoteException/EmptyDirectory.java @@ -0,0 +1,4 @@ +package remoteException; +public class EmptyDirectory extends Exception { + private static final long serialVersionUID = 11L; +} diff --git a/src/remoteException/InternalRemoteError.java b/src/remoteException/InternalRemoteError.java new file mode 100644 index 0000000..9ca91ea --- /dev/null +++ b/src/remoteException/InternalRemoteError.java @@ -0,0 +1,4 @@ +package remoteException; +public class InternalRemoteError extends Exception { + private static final long serialVersionUID = 11L; +} diff --git a/src/exception/NotFound.java b/src/remoteException/NotFound.java similarity index 79% rename from src/exception/NotFound.java rename to src/remoteException/NotFound.java index 7018d43..40c9507 100644 --- a/src/exception/NotFound.java +++ b/src/remoteException/NotFound.java @@ -1,4 +1,4 @@ -package exception; +package remoteException; public class NotFound extends Exception { private static final long serialVersionUID = 11L; } diff --git a/src/remoteException/ProtocolRemoteError.java b/src/remoteException/ProtocolRemoteError.java new file mode 100644 index 0000000..6199f8f --- /dev/null +++ b/src/remoteException/ProtocolRemoteError.java @@ -0,0 +1,4 @@ +package remoteException; +public class ProtocolRemoteError extends Exception { + private static final long serialVersionUID = 11L; +} diff --git a/src/remoteException/VersionRemoteError.java b/src/remoteException/VersionRemoteError.java new file mode 100644 index 0000000..4c372c9 --- /dev/null +++ b/src/remoteException/VersionRemoteError.java @@ -0,0 +1,4 @@ +package remoteException; +public class VersionRemoteError extends Exception { + private static final long serialVersionUID = 11L; +} diff --git a/src/serverP2P/ServerManagementUDP.java b/src/serverP2P/ServerManagementUDP.java index 7f09964..b345b5b 100644 --- a/src/serverP2P/ServerManagementUDP.java +++ b/src/serverP2P/ServerManagementUDP.java @@ -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 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(); initFileList(); - } - - /** 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 + socket = new DatagramSocket(UDPPort); + } catch (SocketException e) { + System.err.println("Error: cannot listen on port " + UDPPort); + System.exit(-1); } } - /** Process the request received. - * @param request the request received - * @return data to be send as response + /** Implementation of runnable. This methods allows to run the server. */ - String processRequest(String request) { - String res = protocolID + "\n"; - String formattedRequest[] = request.split("\n"); - try { + public void run() { + 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 v = new Vector(); 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(); - } - 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"); + (new ProtocolP2PDatagram(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(socket, pd); + } catch (Exception e) { + System.err.println(e); } - 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"; } } + diff --git a/src/serverP2P/ServerP2P.java b/src/serverP2P/ServerP2P.java index 00be061..b22c715 100644 --- a/src/serverP2P/ServerP2P.java +++ b/src/serverP2P/ServerP2P.java @@ -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(); }