From 11fcaa25eaab3c4203ad6caf5791f1ca817e1e01 Mon Sep 17 00:00:00 2001 From: Louis Date: Sat, 29 Feb 2020 16:57:19 +0100 Subject: [PATCH] Add TCP support Fixes #7 --- src/clientP2P/ClientManagementTCP.java | 224 +++++++++++++++++ src/clientP2P/ClientManagementUDP.java | 2 +- src/clientP2P/ClientP2P.java | 11 +- src/protocolP2P/ProtocolP2PPacket.java | 4 +- src/protocolP2P/ProtocolP2PPacketTCP.java | 287 ++++++++++++++++++++++ src/protocolP2P/ProtocolP2PPacketUDP.java | 12 +- src/serverP2P/ServerManagementTCP.java | 185 ++++++++++++++ src/serverP2P/ServerManagementUDP.java | 2 +- src/serverP2P/ServerP2P.java | 13 +- 9 files changed, 721 insertions(+), 19 deletions(-) create mode 100644 src/clientP2P/ClientManagementTCP.java create mode 100644 src/protocolP2P/ProtocolP2PPacketTCP.java create mode 100644 src/serverP2P/ServerManagementTCP.java diff --git a/src/clientP2P/ClientManagementTCP.java b/src/clientP2P/ClientManagementTCP.java new file mode 100644 index 0000000..aaf1e9c --- /dev/null +++ b/src/clientP2P/ClientManagementTCP.java @@ -0,0 +1,224 @@ +package clientP2P; +import exception.InternalError; +import exception.ProtocolError; +import exception.SizeError; +import exception.TransmissionError; +import exception.VersionError; +import remoteException.EmptyFile; +import remoteException.EmptyDirectory; +import remoteException.InternalRemoteError; +import remoteException.NotFound; +import remoteException.ProtocolRemoteError; +import remoteException.VersionRemoteError; +import java.net.UnknownHostException; +import java.util.Scanner; +import java.net.InetAddress; +import java.net.SocketException; +import java.net.Socket; +import java.io.IOException; +import java.nio.file.Files; +import java.io.File; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import protocolP2P.ProtocolP2PPacketTCP; +import protocolP2P.Payload; +import protocolP2P.RequestResponseCode; +import protocolP2P.FileList; +import protocolP2P.FilePart; +import protocolP2P.LoadRequest; + +/** Implementation of P2P-JAVA-PROJECT CLIENT + * @author Louis Royer + * @author Flavien Haas + * @author JS Auge + * @version 1.0 + */ +public class ClientManagementTCP implements Runnable { + private String baseDirectory; + private int TCPPort; + private String host; + private Socket socket; + + + + /** Constructor for TCP implementation, with baseDirectory and TCPPort parameters. + * @param baseDirectory the root directory where files are stored + * @param host hostname of the server + * @param TCPPort the server will listen on this port + */ + public ClientManagementTCP(String baseDirectory, String host, int TCPPort) { + this.baseDirectory = baseDirectory; + this.host = host; + this.TCPPort = TCPPort; + try { + socket = new Socket(InetAddress.getByName(host), TCPPort); + } catch (SocketException e) { + System.err.println("Error: No TCP socket available."); + System.exit(-1); + } catch (UnknownHostException e) { + System.err.println("Error: Unknown host."); + System.exit(-1); + } catch (IOException e) { + System.err.println("Error: Cannot create TCP socket"); + System.exit(-1); + } + } + + /** Implementation of Runnable + */ + public void run() { + try { + String[] list = listDirectory(); + System.out.println("Files present on the server:"); + for(String listItem: list) { + System.out.println(listItem); + } + System.out.println("Name of the file to download:"); + Scanner scanner = new Scanner(System.in); + String f = scanner.nextLine(); + download(f); + System.out.println("File sucessfully downloaded"); + } catch (EmptyDirectory e) { + System.err.println("Error: Server has no file in directory"); + } catch (InternalError e) { + System.err.println("Error: Client internal error"); + } catch (UnknownHostException e) { + System.err.println("Error: Server host is unknown"); + } catch (IOException e) { + System.err.println("Error: Request cannot be send or response cannot be received"); + } catch (TransmissionError e) { + System.err.println("Error: Message received is too big"); + } catch (ProtocolError e) { + System.err.println("Error: Cannot decode server’s response"); + } catch (VersionError e) { + System.err.println("Error: Server’s response use bad version of the protocol"); + } catch (SizeError e) { + System.err.println("Error: Cannot handle this packets because of internal representation limitations of numbers on the client"); + } catch (InternalRemoteError e) { + System.err.println("Error: Server internal error"); + } catch (ProtocolRemoteError e) { + System.err.println("Error: Server cannot decode client’s request"); + } catch (VersionRemoteError e) { + System.err.println("Error: Server cannot decode this version of the protocol"); + } catch (NotFound e) { + System.err.println("Error: Server has not this file in directory"); + } catch (EmptyFile e) { + System.err.println("Error: File is empty"); + } + } + + /** Try to download a file + * @param filename name of the file to download + * @throws NotFound + * @throws InternalError + * @throws UnknownHostException + * @throws IOException + * @throws TransmissionError + * @throws ProtocolError + * @throws VersionError + * @throws SizeError + * @throws InternalRemoteError + * @throws ProtocolRemoteError + * @throws VersionRemoteError + * @throws EmptyFile + */ + private void download(String filename) throws EmptyFile, NotFound, InternalError, UnknownHostException, IOException, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError { + final long MAX_PARTIAL_SIZE = 1024; + ProtocolP2PPacketTCP d = new ProtocolP2PPacketTCP((Payload) new LoadRequest(filename, 0, MAX_PARTIAL_SIZE)); + d.sendRequest((Object)socket); + boolean fileFullyWritten = false; + long offset = 0; + do { + try { + Payload p = d.receiveResponse().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: `" + fp.getFilename() + "`"); + throw new ProtocolError(); + } + if (fp.getOffset() == 0) { + System.err.println("Receiving first partialContent"); + // first partialContent + // increment offset + offset = fp.getPartialContent().length; + /* write first partialContent */ + try { + Files.write(new File(baseDirectory + filename).toPath(), fp.getPartialContent()); + } catch (IOException e) { + System.err.println("Error: cannot write file (" + baseDirectory + filename + ")"); + } + // next partialContentRequest + if (offset != fp.getTotalSize()) { + System.err.println("Sending following request with offset: " + offset + " maxpartialsize: " + MAX_PARTIAL_SIZE); + d = new ProtocolP2PPacketTCP((Payload) new LoadRequest(filename, offset, MAX_PARTIAL_SIZE)); + d.sendRequest((Object)socket); + } else { + fileFullyWritten = true; + } + } else if (offset == fp.getOffset()){ + System.err.println("Receiving following partialContent (offset: " + offset + ")"); + // following + // increment offset + offset += fp.getPartialContent().length; + /* write following partialContent at end of file*/ + try { + Files.write(Paths.get(baseDirectory + filename), fp.getPartialContent(), StandardOpenOption.APPEND); + } catch (IOException e) { + System.err.println("Error: cannot write file (" + baseDirectory + filename + ")"); + } + if (offset >= fp.getTotalSize()) { + fileFullyWritten = true; + } else { + // next partialContentRequest + d = new ProtocolP2PPacketTCP((Payload) new LoadRequest(filename, offset, MAX_PARTIAL_SIZE)); + d.sendRequest((Object)socket); + } + + } else { + System.err.println("offset: " + fp.getOffset() + " ; content.length: " + fp.getPartialContent().length + " ; totalSize: " + fp.getTotalSize()); + System.err.println("Error: cannot handle non-consecutive partial files (not implemented)"); + throw new InternalError(); + } + } + } catch (EmptyDirectory e) { + throw new ProtocolError(); + } + } while(!fileFullyWritten); + } + + /** 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 { + ProtocolP2PPacketTCP d = new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.LIST_REQUEST)); + d.sendRequest((Object)socket); + try { + Payload p = d.receiveResponse().getPayload(); + assert p instanceof FileList : "This payload must be instance of Filelist"; + if (!(p instanceof FileList)) { + throw new InternalError(); + } else { + return ((FileList)p).getFileList(); + } + } catch (NotFound e) { + throw new ProtocolError(); + } catch (EmptyFile e) { + throw new ProtocolError(); + } + } +} diff --git a/src/clientP2P/ClientManagementUDP.java b/src/clientP2P/ClientManagementUDP.java index daf0a8f..3bb9fcb 100644 --- a/src/clientP2P/ClientManagementUDP.java +++ b/src/clientP2P/ClientManagementUDP.java @@ -54,7 +54,7 @@ public class ClientManagementUDP implements Runnable { socket = new DatagramSocket(); socket.connect(InetAddress.getByName(host), UDPPort); } catch (SocketException e) { - System.err.println("Error: No socket available."); + System.err.println("Error: No UDP socket available."); System.exit(-1); } catch (UnknownHostException e) { System.err.println("Error: Unknown host."); diff --git a/src/clientP2P/ClientP2P.java b/src/clientP2P/ClientP2P.java index 8adc662..43d88ee 100644 --- a/src/clientP2P/ClientP2P.java +++ b/src/clientP2P/ClientP2P.java @@ -1,6 +1,6 @@ package clientP2P; import clientP2P.ClientManagementUDP; -//import clientP2P.ClientManagementTCP; +import clientP2P.ClientManagementTCP; import tools.Directories; import java.util.Scanner; @@ -26,6 +26,7 @@ public class ClientP2P { case "UDP": case "udp": case "2" : + System.out.println("Starting with UDP"); ClientManagementUDP cmudp = new ClientManagementUDP(c.directories.getDataHomeDirectory(), c.host, c.port); t = new Thread(cmudp); break; @@ -33,10 +34,10 @@ public class ClientP2P { case "tcp": case "1": default: - //ClientManagementTCP cmtcp = new ClientManagementTCP(c.directories.getDataHomeDirectory(), c.host, c.port); - //t = new Thread(cmtcp); - //break; - throw new java.lang.UnsupportedOperationException(); + System.out.println("Starting with TCP"); + ClientManagementTCP cmtcp = new ClientManagementTCP(c.directories.getDataHomeDirectory(), c.host, c.port); + t = new Thread(cmtcp); + break; } t.setName("client P2P-JAVA-PROJECT"); diff --git a/src/protocolP2P/ProtocolP2PPacket.java b/src/protocolP2P/ProtocolP2PPacket.java index 094c3f9..97df54a 100644 --- a/src/protocolP2P/ProtocolP2PPacket.java +++ b/src/protocolP2P/ProtocolP2PPacket.java @@ -64,7 +64,7 @@ public abstract class ProtocolP2PPacket { public abstract ProtocolP2PPacket receiveResponse() throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException; /** Receive a request, subclasses must overwrite this constructor. - * @param serverSocket socket used to get the request + * @param socket socket used to get the request * @throws TransmissionError * @throws ProtocolError * @throws VersionError @@ -72,7 +72,7 @@ public abstract class ProtocolP2PPacket { * @throws SizeError * @throws IOException */ - protected ProtocolP2PPacket(Object serverSocket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException {} + protected ProtocolP2PPacket(Object socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException {} /** 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 new file mode 100644 index 0000000..cf61767 --- /dev/null +++ b/src/protocolP2P/ProtocolP2PPacketTCP.java @@ -0,0 +1,287 @@ +package protocolP2P; +import exception.InternalError; +import exception.ProtocolError; +import exception.SizeError; +import exception.TransmissionError; +import exception.VersionError; +import remoteException.EmptyDirectory; +import remoteException.InternalRemoteError; +import remoteException.NotFound; +import remoteException.ProtocolRemoteError; +import remoteException.VersionRemoteError; +import remoteException.EmptyFile; +import tools.BytesArrayTools; +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.io.IOException; +import java.io.OutputStream; +import java.net.Socket; + +/** Representation of packet. + * @author Louis Royer + * @author Flavien Haas + * @author JS Auge + * @version 1.0 + */ +public class ProtocolP2PPacketTCP extends ProtocolP2PPacket { + + private Socket responseSocket; // socket used to recept request and send response + private Socket requestSocket; // socket used to send request and to reception response + + /** Constructor with payload parameter (typically used when sending packet). + * @param payload the payload associated with the packet to send + */ + public ProtocolP2PPacketTCP(Payload payload) { + super(payload); + } + + + /** Send a Packet. Socket must be set and connected. + * @param socket Socket used to send Packet. + * @throws InternalError + * @throws IOException + */ + protected void send(Socket socket) throws InternalError, 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())) { + throw new InternalError(); + } + // generate Packet + byte[] packet = toPacket(); + // send it + OutputStream outputStream = socket.getOutputStream(); + outputStream.write(packet); + outputStream.flush(); + } + + /** Send a Request throught socket. Socket must be connected (typically used from client). + * @param socket Socket. Must be connected. + * @throws InternalError + * @throws IOException + */ + public void sendRequest(Object socket) throws InternalError, IOException { + assert socket instanceof Socket: "Wrong socket type"; + if (socket instanceof Socket) { + requestSocket = (Socket)socket; + send(requestSocket); + } else { + throw new InternalError(); + } + } + + /** Receive Request (typically used from server). + * @param socket socket used to receive request + * @throws TransmissionError + * @throws ProtocolError + * @throws VersionError + * @throws InternalError + * @throws SizeError + * @throws IOException + * @return ProtocolP2PPacket received. + */ + public ProtocolP2PPacketTCP(Object socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException { + super(socket); + assert socket instanceof Socket : "Wrong socket type"; + if (!(socket instanceof Socket)) { + throw new InternalError(); + } + Socket ss = (Socket)socket; + byte[] packet = new byte[1024]; + ss.getInputStream().read(packet); + // contruction + boolean protocolError = false; + try { + constructPacket(packet, ss); + Payload payload = getPayload(); + switch (payload.getRequestResponseCode()) { + case PROTOCOL_ERROR : + // we do not want to create an infinite loop of protocolError message exchange. + protocolError = true; + break; + case VERSION_ERROR : + case INTERNAL_ERROR : + case EMPTY_DIRECTORY : + case NOT_FOUND : + case EMPTY_FILE: + case LOAD_RESPONSE: + case LIST_RESPONSE: + // we were expecting a request, but we are receiving a response + throw new ProtocolError(); + default : + break; + } + } catch (TransmissionError e) { + (new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss); + throw e; + } catch (ProtocolError e) { + (new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(ss); + throw e; + } catch (VersionError e) { + (new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.VERSION_ERROR))).send(ss); + throw e; + } catch (InternalError e) { + (new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss); + throw e; + } catch (SizeError e) { + (new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss); + throw e; + } + if (protocolError) { + throw new ProtocolError(); + } + } + + /** Send a Response to a Request (typically used from server). + * @param response Packet to send as a response. + * @throws InternalError + * @throws IOException + */ + public void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException { + assert response instanceof ProtocolP2PPacketTCP: "Wrong Packet type"; + if (response instanceof ProtocolP2PPacketTCP) { + ProtocolP2PPacketTCP r = (ProtocolP2PPacketTCP) response; + assert responseSocket != null : "Cannot send response to a packet not received"; + if (responseSocket == null) { + throw new InternalError(); + } + r.send(responseSocket); + } else { + throw new InternalError(); + } + } + + /** Receive response (typically used by client). + * @return ProtocolP2PPacket received + * @throws EmptyFile + * @throws NotFound + * @throws EmptyDirectory + * @throws InternalRemoteError + * @throws VersionRemoteError + * @throws ProtocolRemoteError + * @throws TransmissionError + * @throws ProtocolError + * @throws VersionError + * @throws InternalError + * @throws SizeError + * @throws IOException + */ + public ProtocolP2PPacket receiveResponse() throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException { + assert requestSocket != null : "Cannot receive response because request packet not sent."; + if (requestSocket == null) { + throw new InternalError(); + } + // reception + byte[] packet = new byte[8192]; + requestSocket.getInputStream().read(packet); + // contruction + try { + ProtocolP2PPacketTCP p = new ProtocolP2PPacketTCP(packet); + Payload payload = p.getPayload(); + switch (payload.getRequestResponseCode()) { + case PROTOCOL_ERROR : + throw new ProtocolRemoteError(); + case VERSION_ERROR : + throw new VersionRemoteError(); + case INTERNAL_ERROR : + throw new InternalRemoteError(); + case EMPTY_DIRECTORY : + throw new EmptyDirectory(); + case NOT_FOUND : + throw new NotFound(); + case EMPTY_FILE: + throw new EmptyFile(); + default : + return (ProtocolP2PPacket)p; + } + } catch (TransmissionError e) { + (new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket); + throw e; + } catch (ProtocolError e) { + (new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(requestSocket); + throw e; + } catch (VersionError e) { + (new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.VERSION_ERROR))).send(requestSocket); + throw e; + } catch (InternalError e) { + (new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket); + throw e; + } catch (SizeError e) { + (new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket); + throw e; + } + } + + /** Private constructor with packet as byte[] parameter (typically used when receiving Packet response). + * @param packet the full Packet received + * @throws TransmissionError + * @throws ProtocolError + * @throws VersionError + * @throws InternalError + * @throws SizeError + */ + private ProtocolP2PPacketTCP(byte[] packet) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError { + super(packet); + constructPacket(packet); + } + + /** Private constructor helper with packet as byte[] parameter (typically used when receiving Packet response/request). + * @param packet the full Packet received + * @throws TransmissionError + * @throws ProtocolError + * @throws VersionError + * @throws InternalError + * @throws SizeError + */ + private void constructPacket(byte[] packet) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError { + // unwrap version + version = packet[VERSION_POSITION]; + checkProtocolVersion(); // this can throw VersionError + RequestResponseCode r = RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]); // this can throw ProtocolError + switch (r) { + case LIST_RESPONSE: + payload = (Payload) new FileList(packet); + break; + case LOAD_RESPONSE: + payload = (Payload) new FilePart(packet); + break; + case LOAD_REQUEST: + payload = (Payload) new LoadRequest(packet); + break; + default: + payload = new Payload(packet); // this can throw TransmissionError + break; + } + } + + /** Private constructor helper with packet as byte[] parameter and (typically used when receiving Packet request). + * @param packet the full Packet received + * @param responseSocket socket address used to reception this request (use this one to respond) + * @throws TransmissionError + * @throws ProtocolError + * @throws VersionError + * @throws InternalError + * @throws SizeError + */ + private void constructPacket(byte[] packet, Socket responseSocket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError { + constructPacket(packet); + this.responseSocket = responseSocket; + } + + /** Returns a byte[] containing full packet (typically used when sending packet). + * This packet is complete and ready to be send. + * @return the full packet to send + * @throws InternalError + */ + protected byte[] toPacket() throws InternalError { + byte[] packet = payload.toPacket(); + packet[VERSION_POSITION] = version; + return packet; + } + +} diff --git a/src/protocolP2P/ProtocolP2PPacketUDP.java b/src/protocolP2P/ProtocolP2PPacketUDP.java index 61fe07f..b434006 100644 --- a/src/protocolP2P/ProtocolP2PPacketUDP.java +++ b/src/protocolP2P/ProtocolP2PPacketUDP.java @@ -90,7 +90,7 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket { } /** Receive Request (typically used from server). - * @param serverSocket socket used to receive request + * @param socket socket used to receive request * @throws TransmissionError * @throws ProtocolError * @throws VersionError @@ -99,13 +99,13 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket { * @throws IOException * @return ProtocolP2PPacket received. */ - public ProtocolP2PPacketUDP(Object serverSocket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException { - super(serverSocket); - assert serverSocket instanceof DatagramSocket : "Wrong socket type"; - if (!(serverSocket instanceof DatagramSocket)) { + public ProtocolP2PPacketUDP(Object socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException { + super(socket); + assert socket instanceof DatagramSocket : "Wrong socket type"; + if (!(socket instanceof DatagramSocket)) { throw new InternalError(); } - DatagramSocket ss = (DatagramSocket)serverSocket; + DatagramSocket ss = (DatagramSocket)socket; byte[] packet = new byte[1024]; DatagramPacket reception = new DatagramPacket(packet, packet.length); ss.receive(reception); diff --git a/src/serverP2P/ServerManagementTCP.java b/src/serverP2P/ServerManagementTCP.java new file mode 100644 index 0000000..ab5f876 --- /dev/null +++ b/src/serverP2P/ServerManagementTCP.java @@ -0,0 +1,185 @@ +package serverP2P; +import java.util.Vector; +import java.io.File; +import java.io.IOException; +import java.net.InetAddress; +import java.net.SocketException; +import java.nio.file.Paths; +import java.nio.file.Files; +import java.net.ServerSocket; +import java.net.Socket; +import protocolP2P.ProtocolP2PPacketTCP; +import protocolP2P.ProtocolP2PPacket; +import protocolP2P.RequestResponseCode; +import protocolP2P.Payload; +import protocolP2P.LoadRequest; +import protocolP2P.FileList; +import protocolP2P.FilePart; +import exception.InternalError; +import exception.ProtocolError; +import exception.SizeError; +import exception.TransmissionError; +import exception.VersionError; +import remoteException.EmptyDirectory; +import remoteException.InternalRemoteError; +import remoteException.NotFound; +import remoteException.ProtocolRemoteError; +import remoteException.VersionRemoteError; +import remoteException.EmptyFile; +import java.util.Arrays; + + +/** Implementation of P2P-JAVA-PROJECT VERSION 1.0 protocol for TCP. + * @author Louis Royer + * @author Flavien Haas + * @author JS Auge + * @version 1.0 + */ +public class ServerManagementTCP implements Runnable { + + private String[] fileList; + private String baseDirectory; + private int TCPPort; + private ServerSocket socket; + + /** Constructor for TCP implementation, with baseDirectory and TCPPort parameters. + * @param baseDirectory the root directory where files are stored + * @param TCPPort the server will listen on this port + */ + public ServerManagementTCP(String baseDirectory, int TCPPort) { + this.baseDirectory = baseDirectory; + this.TCPPort = TCPPort; + initFileList(); + try { + socket = new ServerSocket(TCPPort); + } catch (SocketException e) { + System.err.println("Error: cannot listen on port " + TCPPort + " (TCP)"); + System.exit(-1); + } catch (IOException e) { + System.err.println("Error: cannot openning TCP socket"); + System.exit(-2); + } + } + + /** 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"); + while(true) { + 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); + } + + 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 (IOException e) { + } catch (TransmissionError e) { + } catch (ProtocolError e) { + } catch (VersionError e) { + } catch (InternalError e) { + } catch (SizeError e) { + } + } + } catch (IOException e) { + } + } + + /** 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()) { + v.add(f.getName()); + } + } + fileList = new String[v.size()]; + v.toArray(fileList); + Arrays.sort(fileList); + } + + + + /** Send an internal error message. + * @param pd ProtocolP2PPacketTCP to respond + */ + private void sendInternalError(ProtocolP2PPacketTCP pd) { + try { + pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))); + } catch (Exception e) { + System.err.println(e); + } + } + +} + diff --git a/src/serverP2P/ServerManagementUDP.java b/src/serverP2P/ServerManagementUDP.java index 369a042..946c40d 100644 --- a/src/serverP2P/ServerManagementUDP.java +++ b/src/serverP2P/ServerManagementUDP.java @@ -52,7 +52,7 @@ public class ServerManagementUDP implements Runnable { try { socket = new DatagramSocket(UDPPort); } catch (SocketException e) { - System.err.println("Error: cannot listen on port " + UDPPort); + System.err.println("Error: cannot listen on port " + UDPPort + " (UDP)"); System.exit(-1); } } diff --git a/src/serverP2P/ServerP2P.java b/src/serverP2P/ServerP2P.java index b22c715..cb5ed38 100644 --- a/src/serverP2P/ServerP2P.java +++ b/src/serverP2P/ServerP2P.java @@ -1,5 +1,6 @@ package serverP2P; import serverP2P.ServerManagementUDP; +import serverP2P.ServerManagementTCP; import tools.Directories; public class ServerP2P { @@ -14,10 +15,14 @@ public class ServerP2P { } public static void main(String [] args) { ServerP2P s = new ServerP2P(); - ServerManagementUDP sm = new ServerManagementUDP(s.directories.getDataHomeDirectory(), s.port); - Thread t = new Thread(sm); - t.setName("server P2P-JAVA-PROJECT"); - t.start(); + ServerManagementUDP smudp = new ServerManagementUDP(s.directories.getDataHomeDirectory(), s.port); + ServerManagementTCP smtcp = new ServerManagementTCP(s.directories.getDataHomeDirectory(), s.port); + Thread tudp = new Thread(smudp); + tudp.setName("server UDP P2P-JAVA-PROJECT"); + tudp.start(); + Thread ttcp = new Thread(smtcp); + ttcp.setName("server TCP P2P-JAVA-PROJECT"); + ttcp.start(); } }