Load PartialContents

Fixes #9
pull/3/head
Louis Royer 4 years ago
parent 868d657317
commit d01bbd85f2

@ -4,11 +4,11 @@ All strings in the datagram are utf-8 encoded.
```Datagram format ```Datagram format
1 byte: [0-7 (byte 0 ): VERSION(0x11, first quartet is major version, second is minor)] 1 byte: [(byte 0 ): VERSION(0x11, first quartet is major version, second is minor)]
1 byte: [8-15 (byte 1 ): REQUEST/RESPONSE CODE] 1 byte: [(byte 1 ): REQUEST/RESPONSE CODE]
2 bytes: [16-31 (bytes 2-3): CHECKSUM (UDP only)] 2 bytes: [(bytes 2-3): CHECKSUM (UDP only)]
4 bytes: [32-63 (bytes 4-7): PAYLOAD SIZE IN BYTES] 4 bytes: [(bytes 4-7): PAYLOAD SIZE IN BYTES]
x bytes: [64-xx (bytes 8-?): PAYLOAD] x bytes: [(bytes 8-?): PAYLOAD]
``` ```
@ -46,9 +46,9 @@ Payload size for Not found is zero.
Payload contains Payload contains
``` ```
8 bytes: [64-127 (bytes 8-16): OFFSET OF FILE CONTENT IN BYTES] 8 bytes: [(bytes 8-15): OFFSET OF FILE CONTENT IN BYTES]
8 bytes: [128-191 (bytes 17-24): TOTAL FILESIZE] 8 bytes: [(bytes 16-23): TOTAL FILESIZE]
4 bytes: [192-223 (bytes 25-28): FILENAME SIZE] (cannot be > to PAYLOAD_SIZE - 20 or be zero) 4 bytes: [(bytes 24-27): FILENAME SIZE] (cannot be > to PAYLOAD_SIZE - 20 or be zero)
y bytes: [<FILENAME>] y bytes: [<FILENAME>]
z bytes: [PARTIAL CONTENT] z bytes: [PARTIAL CONTENT]
``` ```
@ -57,9 +57,9 @@ z bytes: [PARTIAL CONTENT]
Payload contains Payload contains
``` ```
8 bytes: [64-127 (bytes 8-16): OFFSET OF FILE CONTENT IN BYTES] 8 bytes: [(bytes 8-15): OFFSET OF FILE CONTENT IN BYTES]
8 bytes: [128-191 (bytes 17-24): MAX SIZE OF PARTIAL CONTENT (partial content in response should not excess this size, but this can be less (by example if endoffile is reached or server doesn't have the full block requested) 8 bytes: [(bytes 16-23): MAX SIZE OF PARTIAL CONTENT (partial content in response should not excess this size, but this can be less (by example if endoffile is reached or server doesn't have the full block requested)
4 bytes: [192-223 (bytes 25-28): FILENAME SIZE] (cannot be > to PAYLOAD_SIZE - 20 or be zero) 4 bytes: [(bytes 24-27): FILENAME SIZE] (cannot be > to PAYLOAD_SIZE - 20 or be zero)
y bytes: [<FILENAME>] y bytes: [<FILENAME>]
``` ```

@ -18,6 +18,8 @@ import java.net.SocketException;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.io.File; import java.io.File;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import protocolP2P.ProtocolP2PDatagram; import protocolP2P.ProtocolP2PDatagram;
import protocolP2P.Payload; import protocolP2P.Payload;
import protocolP2P.RequestResponseCode; import protocolP2P.RequestResponseCode;
@ -115,34 +117,71 @@ public class ClientManagementUDP implements Runnable {
* @throws EmptyFile * @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, TransmissionError, ProtocolError, VersionError, SizeError, InternalRemoteError, ProtocolRemoteError, VersionRemoteError {
ProtocolP2PDatagram d = new ProtocolP2PDatagram((Payload) new LoadRequest(filename)); final long MAX_PARTIAL_SIZE = 1024;
ProtocolP2PDatagram d = new ProtocolP2PDatagram((Payload) new LoadRequest(filename, 0, MAX_PARTIAL_SIZE));
d.send(socket, host, UDPPort); d.send(socket, host, UDPPort);
try { boolean fileFullyWritten = false;
Payload p = ProtocolP2PDatagram.receive(socket).getPayload(); long offset = 0;
assert p instanceof FilePart : "This payload must be instance of FilePart"; do {
if (!(p instanceof FilePart)) { try {
throw new InternalError(); Payload p = ProtocolP2PDatagram.receive(socket).getPayload();
} else { assert p instanceof FilePart : "This payload must be instance of FilePart";
FilePart fp = (FilePart)p; if (!(p instanceof FilePart)) {
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("offset: " + fp.getOffset() + " ; content.length: " + fp.getPartialContent().length + " ; totalSize: " + fp.getTotalSize());
System.err.println("Error: cannot handle partial files (not implemented)");
throw new InternalError(); 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 ProtocolP2PDatagram((Payload) new LoadRequest(filename, offset, MAX_PARTIAL_SIZE));
d.send(socket, host, UDPPort);
} 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 ProtocolP2PDatagram((Payload) new LoadRequest(filename, offset, MAX_PARTIAL_SIZE));
d.send(socket, host, UDPPort);
}
} 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();
}
} }
try { } catch (EmptyDirectory e) {
Files.write(new File(baseDirectory + filename).toPath(), fp.getPartialContent()); throw new ProtocolError();
} catch (IOException e) {
System.err.println("Error: cannot write file (" + baseDirectory + filename + ")");
}
} }
} catch (EmptyDirectory e) { } while(!fileFullyWritten);
throw new ProtocolError();
}
} }
/** list servers directory content /** list servers directory content

@ -20,6 +20,10 @@ public class FilePart extends Payload {
private long totalSize; private long totalSize;
private long offset; private long offset;
private byte[] partialContent; private byte[] partialContent;
static final private int OFFSET_POSITION = 8;
static final private int TOTAL_FILESIZE_POSITION = OFFSET_POSITION + 8;
static final private int FILENAME_SIZE_POSITION = TOTAL_FILESIZE_POSITION + 8;
static final private int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
/** Constructor (typically used by server) with informations about file part to send as parameters. /** Constructor (typically used by server) with informations about file part to send as parameters.
* @param filename name of the file to send * @param filename name of the file to send
@ -73,24 +77,23 @@ public class FilePart extends Payload {
* @throws InternalError * @throws InternalError
*/ */
protected byte[] toDatagram() throws InternalError { protected byte[] toDatagram() throws InternalError {
// compute payload size // compute total size
int size = 28 + filename.length() + partialContent.length; int size = FILENAME_POSITION + filename.length() + partialContent.length;
byte[] datagram = new byte[size]; // java initialize all to zero byte[] datagram = new byte[size + 1]; // java initialize all to zero
// set request/response code // set request/response code
datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue; datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// bits 16-31 are reserved for future use // bits 16-31 are reserved for future use
setPayloadSize(size - 8, datagram); setPayloadSize(size - OFFSET_POSITION, datagram);
// write offset to datagram (Byte 8) // write offset to datagram
BytesArrayTools.write(datagram, 8, offset); BytesArrayTools.write(datagram, OFFSET_POSITION, (long)offset);
// write totalSize to datagram (Byte 16) // write totalSize to datagram
BytesArrayTools.write(datagram, 16, totalSize); BytesArrayTools.write(datagram, TOTAL_FILESIZE_POSITION, (long)totalSize);
// write filenames size to datagram // write filenames size to datagram
BytesArrayTools.write(datagram, 24, filename.length()); BytesArrayTools.write(datagram, FILENAME_SIZE_POSITION, (int)filename.length());
// write filename to datagram
try { try {
byte[] bFilename = filename.getBytes("UTF-8"); // write filename to datagram
int i = filename.length() + 24; int i = FILENAME_POSITION;
for (byte b : bFilename) { for (byte b : filename.getBytes("UTF-8")) {
datagram[i] = b; datagram[i] = b;
i += 1; i += 1;
} }
@ -105,41 +108,41 @@ public class FilePart extends Payload {
} }
} }
/** Write from bytes 8 to 15 of datagram into offset. /** Write from datagram into offset.
* @param datagram received datagram * @param datagram received datagram
* @throws SizeError * @throws SizeError
*/ */
private void setOffset(byte[] datagram) throws SizeError { private void setOffset(byte[] datagram) throws SizeError {
offset = BytesArrayTools.readLong(datagram, 8); offset = BytesArrayTools.readLong(datagram, OFFSET_POSITION);
} }
/** Write from bytes 16 to 23 of datagram into totalSize. /** Write from datagram into totalSize.
* @param datagram received datagram * @param datagram received datagram
* @throws SizeError * @throws SizeError
*/ */
private void setTotalSize(byte[] datagram) throws SizeError { private void setTotalSize(byte[] datagram) throws SizeError {
totalSize = BytesArrayTools.readLong(datagram, 16); totalSize = BytesArrayTools.readLong(datagram, TOTAL_FILESIZE_POSITION);
} }
/** Read filenames size from bytes 24 to 27 of datagram. /** Read filenames size from datagram.
* @param datagram received datagram * @param datagram received datagram
* @throws ProtocolError * @throws ProtocolError
* @throws SizeError * @throws SizeError
* @return filenames size * @return filenames size
*/ */
private int getFilenameSize(byte[] datagram) throws SizeError, ProtocolError { private int getFilenameSize(byte[] datagram) throws SizeError, ProtocolError {
int size = BytesArrayTools.readInt(datagram, 24); // this can throw SizeError int size = BytesArrayTools.readInt(datagram, FILENAME_SIZE_POSITION); // this can throw SizeError
// filename size cannot be zero // filename size cannot be zero
if (size == 0) { if (size == 0) {
throw new ProtocolError(); throw new ProtocolError();
} }
// offset (8B) + totalSize (8B) + filenameSize (4B) = 20B // cannot excess datagram size
if ((20 + size) > getPayloadSize(datagram)) { if ((FILENAME_POSITION + size) > (getPayloadSize(datagram) + OFFSET_POSITION)) {
throw new ProtocolError(); throw new ProtocolError();
} }
return size; return size;
} }
/** Write filename from byte 28 to byte (28 + (filenameSize - 1)) of datagram. /** Write from datagram into filename.
* @param datagram received datagram * @param datagram received datagram
* @throws ProtocolError * @throws ProtocolError
* @throws SizeError * @throws SizeError
@ -148,20 +151,20 @@ public class FilePart extends Payload {
private void setFilename(byte[] datagram) throws ProtocolError, SizeError, InternalError { private void setFilename(byte[] datagram) throws ProtocolError, SizeError, InternalError {
int filenameSize = getFilenameSize(datagram); // this can throw ProtocolError or SizeError int filenameSize = getFilenameSize(datagram); // this can throw ProtocolError or SizeError
try { try {
filename = new String(datagram, 28, filenameSize, "UTF-8"); filename = new String(datagram, FILENAME_POSITION, filenameSize, "UTF-8");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new InternalError(); throw new InternalError();
} }
} }
/** Write partialContent from byte (28 + filenameSize) to byte (8 + payloadSize) of datagram. /** Write from datagram into partialContent.
* @param datagram received datagram * @param datagram received datagram
* @throws SizeError * @throws SizeError
* @throws ProtocolError * @throws ProtocolError
*/ */
private void setPartialContent(byte[] datagram) throws ProtocolError, SizeError { private void setPartialContent(byte[] datagram) throws ProtocolError, SizeError {
int start = 28 + getFilenameSize(datagram); // this can throw SizeError or ProtocolError int start = FILENAME_POSITION + getFilenameSize(datagram); // this can throw SizeError or ProtocolError
int end = 8 + getPayloadSize(datagram); // this can throw SizeError int end = OFFSET_POSITION + getPayloadSize(datagram); // this can throw SizeError
try { try {
partialContent = Arrays.copyOfRange(datagram, start, end); partialContent = Arrays.copyOfRange(datagram, start, end);
} catch (ArrayIndexOutOfBoundsException e) { } catch (ArrayIndexOutOfBoundsException e) {

@ -5,6 +5,7 @@ import exception.TransmissionError;
import exception.ProtocolError; import exception.ProtocolError;
import exception.InternalError; import exception.InternalError;
import exception.SizeError; import exception.SizeError;
import tools.BytesArrayTools;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
/** Representation of payload for load request. /** Representation of payload for load request.
@ -15,12 +16,20 @@ import java.io.UnsupportedEncodingException;
*/ */
public class LoadRequest extends Payload { public class LoadRequest extends Payload {
private String filename; private String filename;
private long maxSizePartialContent;
private long offset;
static final private int OFFSET_POSITION = 8;
static final private int MAX_SIZE_PARTIAL_CONTENT_POSITION = OFFSET_POSITION + 8;
static final private int FILENAME_SIZE_POSITION = MAX_SIZE_PARTIAL_CONTENT_POSITION + 8;
static final private int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
/** Constructor (typically used by the server) with a filename parameter. /** Constructor (typically used by the server) with a filename parameter.
* @param filename name of the file to download. Must not be empty. * @param filename name of the file to download. Must not be empty.
* @param offset offset of the bloc
* @param maxSizePartialContent partial content in response should not excess this size, but this can be less (by example if endoffile is reached or server doesn't have the full block requested)
* @throws InternalError * @throws InternalError
*/ */
public LoadRequest(String filename) throws InternalError { public LoadRequest(String filename, long offset, long maxSizePartialContent) throws InternalError {
super(RequestResponseCode.LOAD_REQUEST); super(RequestResponseCode.LOAD_REQUEST);
/* assert to help debugging */ /* assert to help debugging */
assert filename.length() != 0 : "Payload size of LoadRequest must not be empty"; assert filename.length() != 0 : "Payload size of LoadRequest must not be empty";
@ -28,6 +37,8 @@ public class LoadRequest extends Payload {
throw new InternalError(); throw new InternalError();
} }
this.filename = filename; this.filename = filename;
this.maxSizePartialContent = maxSizePartialContent;
this.offset = offset;
} }
/** Constructor (typically used by client) with a byte[] parameter containing the datagram received. /** Constructor (typically used by client) with a byte[] parameter containing the datagram received.
@ -45,9 +56,16 @@ public class LoadRequest extends Payload {
if (requestResponseCode!= RequestResponseCode.LOAD_REQUEST) { if (requestResponseCode!= RequestResponseCode.LOAD_REQUEST) {
throw new InternalError(); throw new InternalError();
} }
int size = getPayloadSize(datagram); /* Read offset */
offset = BytesArrayTools.readLong(datagram, OFFSET_POSITION);
/* Read maxSizePartialContent */
maxSizePartialContent = BytesArrayTools.readLong(datagram, MAX_SIZE_PARTIAL_CONTENT_POSITION);
/* Read filename */
int size = BytesArrayTools.readInt(datagram, FILENAME_SIZE_POSITION);
try { try {
filename = new String(datagram, 8, size, "UTF-8"); filename = new String(datagram, FILENAME_POSITION, size, "UTF-8");
} catch (UnsupportedEncodingException e) { } catch (UnsupportedEncodingException e) {
throw new InternalError(); throw new InternalError();
} }
@ -61,14 +79,26 @@ public class LoadRequest extends Payload {
*/ */
protected byte[] toDatagram() throws InternalError { protected byte[] toDatagram() throws InternalError {
// compute size // compute size
int size = 8 + filename.length(); int filenameSize = filename.length();
byte[] datagram = new byte[size]; // java initialize all to zero int size = FILENAME_POSITION + filenameSize;
byte[] datagram = new byte[size + 1]; // java initialize all to zero
// set request/response code // set request/response code
datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue; datagram[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// bits 16-31 are reserved for future use // bits 16-31 are reserved for future use
setPayloadSize(size - 8, datagram); // 8 bytes for headers
setPayloadSize(size - OFFSET_POSITION, datagram);
// Write offset
BytesArrayTools.write(datagram, OFFSET_POSITION, (long)offset);
// Write maxSizePartialContent
BytesArrayTools.write(datagram, MAX_SIZE_PARTIAL_CONTENT_POSITION, (long)maxSizePartialContent);
// Write filenameSize
BytesArrayTools.write(datagram, FILENAME_SIZE_POSITION, (int)filenameSize);
// Write filename // Write filename
int bCount = 8; int bCount = FILENAME_POSITION;
try { try {
byte[] sb = filename.getBytes("UTF-8"); byte[] sb = filename.getBytes("UTF-8");
for(byte b : sb) { for(byte b : sb) {
@ -87,4 +117,18 @@ public class LoadRequest extends Payload {
public String getFilename() { public String getFilename() {
return filename; return filename;
} }
/** offset getter.
* @return offset
*/
public long getOffset() {
return offset;
}
/** maxSizePartialContent getter.
* @return maxSizePartialContent
*/
public long getMaxSizePartialContent() {
return maxSizePartialContent;
}
} }

@ -115,7 +115,7 @@ public class ProtocolP2PDatagram {
*/ */
public static ProtocolP2PDatagram receive(DatagramSocket socket) throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException { public static ProtocolP2PDatagram receive(DatagramSocket socket) throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException {
// reception // reception
byte[] datagram = new byte[4096]; byte[] datagram = new byte[4096*2];
DatagramPacket reception = new DatagramPacket(datagram, datagram.length); DatagramPacket reception = new DatagramPacket(datagram, datagram.length);
socket.receive(reception); socket.receive(reception);
// contruction // contruction

@ -70,19 +70,37 @@ public class ServerManagementUDP implements Runnable {
sendInternalError(pd); sendInternalError(pd);
} else { } else {
String filename = ((LoadRequest)p).getFilename(); String filename = ((LoadRequest)p).getFilename();
long offset = ((LoadRequest)p).getOffset();
long maxSizePartialContent = ((LoadRequest)p).getMaxSizePartialContent();
try { try {
byte[] load = Files.readAllBytes(Paths.get(baseDirectory + filename)); 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) { if (Arrays.binarySearch(fileList, filename) >= 0) {
try { try {
if (load.length == 0) { if (load.length == 0) {
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.EMPTY_FILE))).send(socket, pd); (new ProtocolP2PDatagram(new Payload(RequestResponseCode.EMPTY_FILE))).send(socket, pd);
} else { } else {
(new ProtocolP2PDatagram((Payload)(new FilePart(filename, load.length, 0, load)))).send(socket, pd); (new ProtocolP2PDatagram((Payload)(new FilePart(filename, fullLoad.length, offset, load)))).send(socket, pd);
} }
} catch (Exception e2) { } catch (Exception e2) {
System.err.println(e2); System.err.println(e2);
} }
} else { } 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 throw new IOException(); // to send a NOT_FOUND in the catch block
} }
} catch (IOException e) { } catch (IOException e) {
@ -141,6 +159,7 @@ public class ServerManagementUDP implements Runnable {
} }
fileList = new String[v.size()]; fileList = new String[v.size()];
v.toArray(fileList); v.toArray(fileList);
Arrays.sort(fileList);
} }

Loading…
Cancel
Save