package clientP2P; import exception.InternalError; import exception.ProtocolError; import exception.SizeError; import exception.TransmissionError; import exception.VersionError; import remoteException.EmptyFile; import remoteException.EmptyDirectory; import remoteException.InternalRemoteError; import remoteException.NotFound; import remoteException.ProtocolRemoteError; import remoteException.VersionRemoteError; import java.net.UnknownHostException; import java.util.Scanner; import java.net.InetAddress; import java.net.DatagramSocket; import java.net.SocketException; import java.io.IOException; import java.nio.file.Files; import java.io.File; import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import protocolP2P.ProtocolP2PPacketUDP; import protocolP2P.Payload; import protocolP2P.RequestResponseCode; import protocolP2P.FileList; import protocolP2P.FilePart; import protocolP2P.LoadRequest; /** Implementation of P2P-JAVA-PROJECT CLIENT * @author Louis Royer * @author Flavien Haas * @author JS Auge * @version 1.0 */ public class ClientManagementUDP implements Runnable { private String baseDirectory; private int UDPPort; private String host; private DatagramSocket socket; /** Constructor for UDP implementation, with baseDirectory and UDPPort parameters. * @param baseDirectory the root directory where files are stored * @param host hostname of the server * @param UDPPort the server will listen on this port */ public ClientManagementUDP(String baseDirectory, String host, int UDPPort) { this.baseDirectory = baseDirectory; this.host = host; this.UDPPort = UDPPort; try { socket = new DatagramSocket(); socket.connect(InetAddress.getByName(host), UDPPort); } catch (SocketException e) { System.err.println("Error: No UDP socket available."); System.exit(-1); } catch (UnknownHostException e) { System.err.println("Error: Unknown host."); 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 = 4096; ProtocolP2PPacketUDP d = new ProtocolP2PPacketUDP((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 ProtocolP2PPacketUDP((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 ProtocolP2PPacketUDP((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 { ProtocolP2PPacketUDP d = new ProtocolP2PPacketUDP(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(); } } }