From 11901db82236425941a31f2f156d857678701c95 Mon Sep 17 00:00:00 2001 From: Louis Date: Tue, 3 Mar 2020 16:39:19 +0100 Subject: [PATCH] Fixes #19, Fixes #13 --- src/clientP2P/ClientManagementTCP.java | 16 +- src/exception/SocketClosed.java | 4 + src/protocolP2P/ProtocolP2PPacket.java | 13 +- src/protocolP2P/ProtocolP2PPacketTCP.java | 47 ++++- src/protocolP2P/ProtocolP2PPacketUDP.java | 4 +- src/serverP2P/ServerManagementTCP.java | 220 +++++++++++++--------- src/serverP2P/ServerManagementUDP.java | 2 + src/serverP2P/ServerP2P.java | 1 + 8 files changed, 205 insertions(+), 102 deletions(-) create mode 100644 src/exception/SocketClosed.java diff --git a/src/clientP2P/ClientManagementTCP.java b/src/clientP2P/ClientManagementTCP.java index d220d7b..3c7f4b0 100644 --- a/src/clientP2P/ClientManagementTCP.java +++ b/src/clientP2P/ClientManagementTCP.java @@ -4,6 +4,7 @@ import exception.ProtocolError; import exception.SizeError; import exception.TransmissionError; import exception.VersionError; +import exception.SocketClosed; import remoteException.EmptyFile; import remoteException.EmptyDirectory; import remoteException.InternalRemoteError; @@ -84,6 +85,8 @@ public class ClientManagementTCP implements Runnable { System.err.println("Error: Client internal error"); } catch (UnknownHostException e) { System.err.println("Error: Server host is unknown"); + } catch (SocketClosed e) { + System.err.println("Error: Request cannot be send or response cannot be received"); } catch (IOException e) { System.err.println("Error: Request cannot be send or response cannot be received"); } catch (TransmissionError e) { @@ -104,6 +107,13 @@ public class ClientManagementTCP implements Runnable { System.err.println("Error: Server has not this file in directory"); } catch (EmptyFile e) { System.err.println("Error: File is empty"); + } finally { + try { + System.err.println("Closing socket"); + socket.close(); + } catch (IOException e2) { + System.err.println("Error: cannot close socket"); + } } } @@ -113,6 +123,7 @@ public class ClientManagementTCP implements Runnable { * @throws InternalError * @throws UnknownHostException * @throws IOException + * @throws SocketClosed * @throws TransmissionError * @throws ProtocolError * @throws VersionError @@ -122,7 +133,7 @@ public class ClientManagementTCP implements Runnable { * @throws VersionRemoteError * @throws EmptyFile */ - private void download(String filename) throws EmptyFile, NotFound, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError { + private void download(String filename) throws EmptyFile, NotFound, InternalError, UnknownHostException, IOException, SocketClosed, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError { final long MAX_PARTIAL_SIZE = 4096; ProtocolP2PPacketTCP d = new ProtocolP2PPacketTCP((Payload) new LoadRequest(filename, 0, MAX_PARTIAL_SIZE)); d.sendRequest((Object)socket); @@ -195,6 +206,7 @@ public class ClientManagementTCP implements Runnable { * @return list of files * @throws InternalError * @throws UnknowHostException + * @throws SocketClosed * @throws IOException * @throws TransmissionError * @throws ProtocolError @@ -205,7 +217,7 @@ public class ClientManagementTCP implements Runnable { * @throws ProtocolRemoteError * @throws VersionRemoteError */ - private String[] listDirectory() throws EmptyDirectory, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError { + private String[] listDirectory() throws EmptyDirectory, InternalError, UnknownHostException, SocketClosed, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError { ProtocolP2PPacketTCP d = new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.LIST_REQUEST)); d.sendRequest((Object)socket); try { diff --git a/src/exception/SocketClosed.java b/src/exception/SocketClosed.java new file mode 100644 index 0000000..4972843 --- /dev/null +++ b/src/exception/SocketClosed.java @@ -0,0 +1,4 @@ +package exception; +public class SocketClosed extends Exception { + private static final long serialVersionUID = 12L; +} diff --git a/src/protocolP2P/ProtocolP2PPacket.java b/src/protocolP2P/ProtocolP2PPacket.java index 97df54a..d1298d5 100644 --- a/src/protocolP2P/ProtocolP2PPacket.java +++ b/src/protocolP2P/ProtocolP2PPacket.java @@ -4,6 +4,7 @@ import exception.ProtocolError; import exception.SizeError; import exception.TransmissionError; import exception.VersionError; +import exception.SocketClosed; import remoteException.EmptyDirectory; import remoteException.InternalRemoteError; import remoteException.NotFound; @@ -37,15 +38,17 @@ public abstract class ProtocolP2PPacket { * @param socket Socket used to send packet. * @throws InternalError * @throws IOException + * @throws SocketClosed */ - public abstract void sendRequest(Object socket) throws InternalError, IOException; + public abstract void sendRequest(Object socket) throws InternalError, IOException, SocketClosed; /** Send a response * @param response Response to send. * @throws InternalError * @throws IOException + * @throws SocketClosed */ - public abstract void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException; + public abstract void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException, SocketClosed; /** Receive a response * @throws EmptyFile @@ -60,8 +63,9 @@ public abstract class ProtocolP2PPacket { * @throws InternalError * @throws SizeError * @throws IOException + * @throws SocketClosed */ - public abstract ProtocolP2PPacket receiveResponse() throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException; + public abstract ProtocolP2PPacket receiveResponse() throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, SocketClosed; /** Receive a request, subclasses must overwrite this constructor. * @param socket socket used to get the request @@ -71,8 +75,9 @@ public abstract class ProtocolP2PPacket { * @throws InternalError * @throws SizeError * @throws IOException + * @throws SocketClosed */ - protected ProtocolP2PPacket(Object socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException {} + protected ProtocolP2PPacket(Object socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, SocketClosed {} /** Construct a packet from byte[], subclasses must overwrite this constructor. * @param packet Packet received diff --git a/src/protocolP2P/ProtocolP2PPacketTCP.java b/src/protocolP2P/ProtocolP2PPacketTCP.java index cf61767..3f2dfc5 100644 --- a/src/protocolP2P/ProtocolP2PPacketTCP.java +++ b/src/protocolP2P/ProtocolP2PPacketTCP.java @@ -4,6 +4,7 @@ import exception.ProtocolError; import exception.SizeError; import exception.TransmissionError; import exception.VersionError; +import exception.SocketClosed; import remoteException.EmptyDirectory; import remoteException.InternalRemoteError; import remoteException.NotFound; @@ -44,9 +45,10 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket { /** Send a Packet. Socket must be set and connected. * @param socket Socket used to send Packet. * @throws InternalError + * @throws SocketClosed * @throws IOException */ - protected void send(Socket socket) throws InternalError, IOException { + protected void send(Socket socket) throws InternalError, SocketClosed, IOException { assert socket != null : "Trying to send a Packet but no socket defined"; assert socket.isConnected() : "Trying to send a Packet but socket not connected"; if (socket == null || (!socket.isConnected())) { @@ -55,17 +57,30 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket { // generate Packet byte[] packet = toPacket(); // send it - OutputStream outputStream = socket.getOutputStream(); - outputStream.write(packet); - outputStream.flush(); + try { + OutputStream outputStream = socket.getOutputStream(); + outputStream.write(packet); + outputStream.flush(); + } catch (IOException e) { + // closing socket + System.err.println("Error: cannot send response, closing socket"); + try { + socket.close(); + } catch (IOException e2) { + System.err.println("Cannot close socket"); + } finally { + throw new SocketClosed(); + } + } } /** Send a Request throught socket. Socket must be connected (typically used from client). * @param socket Socket. Must be connected. * @throws InternalError + * @throws SocketClosed * @throws IOException */ - public void sendRequest(Object socket) throws InternalError, IOException { + public void sendRequest(Object socket) throws InternalError, IOException, SocketClosed { assert socket instanceof Socket: "Wrong socket type"; if (socket instanceof Socket) { requestSocket = (Socket)socket; @@ -82,10 +97,11 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket { * @throws VersionError * @throws InternalError * @throws SizeError + * @throws SocketClosed * @throws IOException * @return ProtocolP2PPacket received. */ - public ProtocolP2PPacketTCP(Object socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException { + public ProtocolP2PPacketTCP(Object socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, SocketClosed, IOException { super(socket); assert socket instanceof Socket : "Wrong socket type"; if (!(socket instanceof Socket)) { @@ -93,7 +109,18 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket { } Socket ss = (Socket)socket; byte[] packet = new byte[1024]; - ss.getInputStream().read(packet); + try { + System.err.println("Reading " + ss.getInputStream().read(packet) + " bytes"); + } catch (IOException e) { + System.err.println("Error: cannot read request, closing socket"); + try { + ss.close(); + } catch (IOException e2) { + System.err.println("Cannot close socket"); + } finally { + throw new SocketClosed(); + } + } // contruction boolean protocolError = false; try { @@ -141,8 +168,9 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket { * @param response Packet to send as a response. * @throws InternalError * @throws IOException + * @throws SocketClosed */ - public void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException { + public void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException, SocketClosed { assert response instanceof ProtocolP2PPacketTCP: "Wrong Packet type"; if (response instanceof ProtocolP2PPacketTCP) { ProtocolP2PPacketTCP r = (ProtocolP2PPacketTCP) response; @@ -170,8 +198,9 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket { * @throws InternalError * @throws SizeError * @throws IOException + * @throws SocketClosed */ - public ProtocolP2PPacket receiveResponse() throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException { + public ProtocolP2PPacket receiveResponse() throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, SocketClosed { assert requestSocket != null : "Cannot receive response because request packet not sent."; if (requestSocket == null) { throw new InternalError(); diff --git a/src/protocolP2P/ProtocolP2PPacketUDP.java b/src/protocolP2P/ProtocolP2PPacketUDP.java index b434006..7237da9 100644 --- a/src/protocolP2P/ProtocolP2PPacketUDP.java +++ b/src/protocolP2P/ProtocolP2PPacketUDP.java @@ -4,6 +4,7 @@ import exception.ProtocolError; import exception.SizeError; import exception.TransmissionError; import exception.VersionError; +import exception.SocketClosed; import remoteException.EmptyDirectory; import remoteException.InternalRemoteError; import remoteException.NotFound; @@ -97,9 +98,10 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket { * @throws InternalError * @throws SizeError * @throws IOException + * @throws SocketClosed * @return ProtocolP2PPacket received. */ - public ProtocolP2PPacketUDP(Object socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException { + public ProtocolP2PPacketUDP(Object socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, SocketClosed { super(socket); assert socket instanceof DatagramSocket : "Wrong socket type"; if (!(socket instanceof DatagramSocket)) { diff --git a/src/serverP2P/ServerManagementTCP.java b/src/serverP2P/ServerManagementTCP.java index ba04f4e..f62e2c0 100644 --- a/src/serverP2P/ServerManagementTCP.java +++ b/src/serverP2P/ServerManagementTCP.java @@ -20,6 +20,7 @@ import exception.ProtocolError; import exception.SizeError; import exception.TransmissionError; import exception.VersionError; +import exception.SocketClosed; import remoteException.EmptyDirectory; import remoteException.InternalRemoteError; import remoteException.NotFound; @@ -64,93 +65,37 @@ public class ServerManagementTCP implements Runnable { /** Implementation of runnable. This methods allows to run the server. */ public void run() { - // TODO: handling multiple clients - try { - Socket s = socket.accept(); - System.err.println("Accepting new connection"); - do { - try { - ProtocolP2PPacketTCP pd = new ProtocolP2PPacketTCP((Object)s); - 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(); - long offset = ((LoadRequest)p).getOffset(); - long maxSizePartialContent = ((LoadRequest)p).getMaxSizePartialContent(); - try { - byte[] fullLoad = Files.readAllBytes(Paths.get(baseDirectory + filename)); - long sizeToSend = 0; - if (fullLoad.length - offset < maxSizePartialContent) { - System.out.println("Sending last partialContent"); - sizeToSend = fullLoad.length - offset; - } else { - sizeToSend = maxSizePartialContent; - } - System.out.println("maxSizePartialContent: " + maxSizePartialContent); - System.out.println("Sending " + filename + " from " + offset + " to " + (offset + sizeToSend)); - byte[] load = Arrays.copyOfRange(fullLoad, (int)offset, (int)(offset + sizeToSend)); - if (Arrays.binarySearch(fileList, filename) >= 0) { - try { - if (load.length == 0) { - pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.EMPTY_FILE))); - } else { - pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new FilePart(filename, fullLoad.length, offset, load)))); - } - } catch (Exception e2) { - System.err.println(e2); - } - } else { - System.err.println("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename)); - System.err.println("File list:"); - for (String f: fileList) { - System.err.println("- " + f); - } + do { + try { + Socket s = socket.accept(); + System.err.println("Accepting new connection"); + ClientHandler c = new ClientHandler(s); + (new Thread(c)).start(); + } catch (IOException e) { + System.err.println("Error while accepting new connection"); + } + } while(true); + } - throw new IOException(); // to send a NOT_FOUND in the catch block - } - } catch (IOException e) { - try { - pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_FOUND))); - } 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"); - pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.EMPTY_DIRECTORY))); - } else { - System.out.println("Sending LIST_RESPONSE"); - pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new FileList(fileList)))); - } - } catch (Exception e2) { - System.err.println(e2); - } - break; - default: - sendInternalError(pd); - } - } catch (SocketException e) { - System.out.println("connection closed"); - s.close(); - } - catch (IOException e) {} - catch (TransmissionError e) {} - catch (ProtocolError e) {} - catch (VersionError e) {} - catch (InternalError e) {} - catch (SizeError e) {} - } while(true); - } catch (IOException e) { + /** Private runnable class allowing to serve one client. + */ + private class ClientHandler implements Runnable { + Socket s; + /** Constructor with a socket. + * @param s Socket of this client + */ + public ClientHandler(Socket s) { + this.s = s; + } + + /** Implementation of runnable. This method allow to serve one client. + */ + public void run() { + boolean end = false; + do { + end = handleRequest(s); + } while(!end); + System.err.println("End of thread"); } } @@ -182,4 +127,107 @@ public class ServerManagementTCP implements Runnable { } } + /** Send response to list request + * @param pd Request received + */ + private void sendListResponse(ProtocolP2PPacketTCP pd) { + try { + if (fileList.length == 0) { + System.err.println("Sending EMPTY_DIRECTORY"); + pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.EMPTY_DIRECTORY))); + } else { + System.out.println("Sending LIST_RESPONSE"); + pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new FileList(fileList)))); + } + } catch (Exception e2) { + System.err.println(e2); + } + } + + /** Send response to load request + * @param pd Request received + */ + private void sendLoadResponse(ProtocolP2PPacketTCP pd) { + Payload p = pd.getPayload(); + assert p instanceof LoadRequest : "payload must be an instance of LoadRequest"; + if (!(p instanceof LoadRequest)) { + sendInternalError(pd); + } else { + String filename = ((LoadRequest)p).getFilename(); + long offset = ((LoadRequest)p).getOffset(); + long maxSizePartialContent = ((LoadRequest)p).getMaxSizePartialContent(); + try { + byte[] fullLoad = Files.readAllBytes(Paths.get(baseDirectory + filename)); + long sizeToSend = 0; + if (fullLoad.length - offset < maxSizePartialContent) { + System.out.println("Sending last partialContent"); + sizeToSend = fullLoad.length - offset; + } else { + sizeToSend = maxSizePartialContent; + } + System.out.println("maxSizePartialContent: " + maxSizePartialContent); + System.out.println("Sending " + filename + " from " + offset + " to " + (offset + sizeToSend)); + byte[] load = Arrays.copyOfRange(fullLoad, (int)offset, (int)(offset + sizeToSend)); + if (Arrays.binarySearch(fileList, filename) >= 0) { + try { + if (load.length == 0) { + pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.EMPTY_FILE))); + } else { + pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP((Payload)(new FilePart(filename, fullLoad.length, offset, load)))); + } + } catch (Exception e2) { + System.err.println(e2); + } + } else { + System.err.println("File requested not found: `" + filename + "` " + Arrays.binarySearch(fileList, filename)); + System.err.println("File list:"); + for (String f: fileList) { + System.err.println("- " + f); + } + + throw new IOException(); // to send a NOT_FOUND in the catch block + } + } catch (IOException e) { + try { + pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_FOUND))); + } catch (Exception e2) { + System.err.println(e2); + } + } + } + } + + /** Respond to next request incomming on socket s. + * @param s Socket used to read request and send response + * @return true if cannot expect another request (ie, socket is closed) + */ + private boolean handleRequest(Socket s) { + try { + ProtocolP2PPacketTCP pd = new ProtocolP2PPacketTCP((Object)s); + Payload p = pd.getPayload(); + switch (p.getRequestResponseCode()) { + case LOAD_REQUEST: + System.out.println("Received LOAD_REQUEST"); + sendLoadResponse(pd); + break; + case LIST_REQUEST: + System.out.println("Received LIST_REQUEST"); + sendListResponse(pd); + break; + default: + sendInternalError(pd); + } + } catch (IOException e) { + return true; + } catch (SocketClosed e) { + return true; + } + catch (TransmissionError e) {} + catch (ProtocolError e) {} + catch (VersionError e) {} + catch (InternalError e) {} + catch (SizeError e) {} + return false; + } + } diff --git a/src/serverP2P/ServerManagementUDP.java b/src/serverP2P/ServerManagementUDP.java index 946c40d..a96138b 100644 --- a/src/serverP2P/ServerManagementUDP.java +++ b/src/serverP2P/ServerManagementUDP.java @@ -19,6 +19,7 @@ import exception.ProtocolError; import exception.SizeError; import exception.TransmissionError; import exception.VersionError; +import exception.SocketClosed; import remoteException.EmptyDirectory; import remoteException.InternalRemoteError; import remoteException.NotFound; @@ -132,6 +133,7 @@ public class ServerManagementUDP implements Runnable { sendInternalError(pd); } } catch (IOException e) { + } catch (SocketClosed e) { } catch (TransmissionError e) { } catch (ProtocolError e) { } catch (VersionError e) { diff --git a/src/serverP2P/ServerP2P.java b/src/serverP2P/ServerP2P.java index cb5ed38..52ef26c 100644 --- a/src/serverP2P/ServerP2P.java +++ b/src/serverP2P/ServerP2P.java @@ -23,6 +23,7 @@ public class ServerP2P { Thread ttcp = new Thread(smtcp); ttcp.setName("server TCP P2P-JAVA-PROJECT"); ttcp.start(); + System.out.println("Server started."); } }