Load PartialContents

Fixes #9
This commit is contained in:
Louis Royer 2020-02-26 11:10:34 +01:00
parent 868d657317
commit d01bbd85f2
6 changed files with 176 additions and 71 deletions

View File

@ -4,11 +4,11 @@ All strings in the datagram are utf-8 encoded.
```Datagram format
1 byte: [0-7 (byte 0 ): VERSION(0x11, first quartet is major version, second is minor)]
1 byte: [8-15 (byte 1 ): REQUEST/RESPONSE CODE]
2 bytes: [16-31 (bytes 2-3): CHECKSUM (UDP only)]
4 bytes: [32-63 (bytes 4-7): PAYLOAD SIZE IN BYTES]
x bytes: [64-xx (bytes 8-?): PAYLOAD]
1 byte: [(byte 0 ): VERSION(0x11, first quartet is major version, second is minor)]
1 byte: [(byte 1 ): REQUEST/RESPONSE CODE]
2 bytes: [(bytes 2-3): CHECKSUM (UDP only)]
4 bytes: [(bytes 4-7): PAYLOAD SIZE IN BYTES]
x bytes: [(bytes 8-?): PAYLOAD]
```
@ -46,9 +46,9 @@ Payload size for Not found is zero.
Payload contains
```
8 bytes: [64-127 (bytes 8-16): OFFSET OF FILE CONTENT IN BYTES]
8 bytes: [128-191 (bytes 17-24): TOTAL FILESIZE]
4 bytes: [192-223 (bytes 25-28): FILENAME SIZE] (cannot be > to PAYLOAD_SIZE - 20 or be zero)
8 bytes: [(bytes 8-15): OFFSET OF FILE CONTENT IN BYTES]
8 bytes: [(bytes 16-23): TOTAL FILESIZE]
4 bytes: [(bytes 24-27): FILENAME SIZE] (cannot be > to PAYLOAD_SIZE - 20 or be zero)
y bytes: [<FILENAME>]
z bytes: [PARTIAL CONTENT]
```
@ -57,9 +57,9 @@ z bytes: [PARTIAL CONTENT]
Payload contains
```
8 bytes: [64-127 (bytes 8-16): 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)
4 bytes: [192-223 (bytes 25-28): FILENAME SIZE] (cannot be > to PAYLOAD_SIZE - 20 or be zero)
8 bytes: [(bytes 8-15): OFFSET OF FILE CONTENT IN BYTES]
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: [(bytes 24-27): FILENAME SIZE] (cannot be > to PAYLOAD_SIZE - 20 or be zero)
y bytes: [<FILENAME>]
```

View File

@ -18,6 +18,8 @@ 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.ProtocolP2PDatagram;
import protocolP2P.Payload;
import protocolP2P.RequestResponseCode;
@ -115,34 +117,71 @@ public class ClientManagementUDP implements Runnable {
* @throws EmptyFile
*/
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);
try {
Payload p = ProtocolP2PDatagram.receive(socket).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");
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)");
boolean fileFullyWritten = false;
long offset = 0;
do {
try {
Payload p = ProtocolP2PDatagram.receive(socket).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 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 {
Files.write(new File(baseDirectory + filename).toPath(), fp.getPartialContent());
} catch (IOException e) {
System.err.println("Error: cannot write file (" + baseDirectory + filename + ")");
}
} catch (EmptyDirectory e) {
throw new ProtocolError();
}
} catch (EmptyDirectory e) {
throw new ProtocolError();
}
} while(!fileFullyWritten);
}
/** list servers directory content

View File

@ -20,6 +20,10 @@ public class FilePart extends Payload {
private long totalSize;
private long offset;
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.
* @param filename name of the file to send
@ -73,24 +77,23 @@ public class FilePart extends Payload {
* @throws InternalError
*/
protected byte[] toDatagram() throws InternalError {
// compute payload size
int size = 28 + filename.length() + partialContent.length;
byte[] datagram = new byte[size]; // java initialize all to zero
// compute total size
int size = FILENAME_POSITION + filename.length() + partialContent.length;
byte[] datagram = new byte[size + 1]; // 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 offset to datagram (Byte 8)
BytesArrayTools.write(datagram, 8, offset);
// write totalSize to datagram (Byte 16)
BytesArrayTools.write(datagram, 16, totalSize);
setPayloadSize(size - OFFSET_POSITION, datagram);
// write offset to datagram
BytesArrayTools.write(datagram, OFFSET_POSITION, (long)offset);
// write totalSize to datagram
BytesArrayTools.write(datagram, TOTAL_FILESIZE_POSITION, (long)totalSize);
// write filenames size to datagram
BytesArrayTools.write(datagram, 24, filename.length());
// write filename to datagram
BytesArrayTools.write(datagram, FILENAME_SIZE_POSITION, (int)filename.length());
try {
byte[] bFilename = filename.getBytes("UTF-8");
int i = filename.length() + 24;
for (byte b : bFilename) {
// write filename to datagram
int i = FILENAME_POSITION;
for (byte b : filename.getBytes("UTF-8")) {
datagram[i] = b;
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
* @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
* @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
* @throws ProtocolError
* @throws SizeError
* @return filenames size
*/
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
if (size == 0) {
throw new ProtocolError();
}
// offset (8B) + totalSize (8B) + filenameSize (4B) = 20B
if ((20 + size) > getPayloadSize(datagram)) {
// cannot excess datagram size
if ((FILENAME_POSITION + size) > (getPayloadSize(datagram) + OFFSET_POSITION)) {
throw new ProtocolError();
}
return size;
}
/** Write filename from byte 28 to byte (28 + (filenameSize - 1)) of datagram.
/** Write from datagram into filename.
* @param datagram received datagram
* @throws ProtocolError
* @throws SizeError
@ -148,20 +151,20 @@ public class FilePart extends Payload {
private void setFilename(byte[] datagram) throws ProtocolError, SizeError, InternalError {
int filenameSize = getFilenameSize(datagram); // this can throw ProtocolError or SizeError
try {
filename = new String(datagram, 28, filenameSize, "UTF-8");
filename = new String(datagram, FILENAME_POSITION, filenameSize, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new InternalError();
}
}
/** Write partialContent from byte (28 + filenameSize) to byte (8 + payloadSize) of datagram.
/** Write from datagram into partialContent.
* @param datagram received datagram
* @throws SizeError
* @throws ProtocolError
*/
private void setPartialContent(byte[] datagram) throws ProtocolError, SizeError {
int start = 28 + getFilenameSize(datagram); // this can throw SizeError or ProtocolError
int end = 8 + getPayloadSize(datagram); // this can throw SizeError
int start = FILENAME_POSITION + getFilenameSize(datagram); // this can throw SizeError or ProtocolError
int end = OFFSET_POSITION + getPayloadSize(datagram); // this can throw SizeError
try {
partialContent = Arrays.copyOfRange(datagram, start, end);
} catch (ArrayIndexOutOfBoundsException e) {

View File

@ -5,6 +5,7 @@ import exception.TransmissionError;
import exception.ProtocolError;
import exception.InternalError;
import exception.SizeError;
import tools.BytesArrayTools;
import java.io.UnsupportedEncodingException;
/** Representation of payload for load request.
@ -15,12 +16,20 @@ import java.io.UnsupportedEncodingException;
*/
public class LoadRequest extends Payload {
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.
* @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
*/
public LoadRequest(String filename) throws InternalError {
public LoadRequest(String filename, long offset, long maxSizePartialContent) throws InternalError {
super(RequestResponseCode.LOAD_REQUEST);
/* assert to help debugging */
assert filename.length() != 0 : "Payload size of LoadRequest must not be empty";
@ -28,6 +37,8 @@ public class LoadRequest extends Payload {
throw new InternalError();
}
this.filename = filename;
this.maxSizePartialContent = maxSizePartialContent;
this.offset = offset;
}
/** 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) {
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 {
filename = new String(datagram, 8, size, "UTF-8");
filename = new String(datagram, FILENAME_POSITION, size, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new InternalError();
}
@ -61,14 +79,26 @@ public class LoadRequest extends Payload {
*/
protected byte[] toDatagram() throws InternalError {
// compute size
int size = 8 + filename.length();
byte[] datagram = new byte[size]; // java initialize all to zero
int filenameSize = filename.length();
int size = FILENAME_POSITION + filenameSize;
byte[] datagram = new byte[size + 1]; // 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);
// 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
int bCount = 8;
int bCount = FILENAME_POSITION;
try {
byte[] sb = filename.getBytes("UTF-8");
for(byte b : sb) {
@ -87,4 +117,18 @@ public class LoadRequest extends Payload {
public String getFilename() {
return filename;
}
/** offset getter.
* @return offset
*/
public long getOffset() {
return offset;
}
/** maxSizePartialContent getter.
* @return maxSizePartialContent
*/
public long getMaxSizePartialContent() {
return maxSizePartialContent;
}
}

View File

@ -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 {
// reception
byte[] datagram = new byte[4096];
byte[] datagram = new byte[4096*2];
DatagramPacket reception = new DatagramPacket(datagram, datagram.length);
socket.receive(reception);
// contruction

View File

@ -70,19 +70,37 @@ public class ServerManagementUDP implements Runnable {
sendInternalError(pd);
} else {
String filename = ((LoadRequest)p).getFilename();
long offset = ((LoadRequest)p).getOffset();
long maxSizePartialContent = ((LoadRequest)p).getMaxSizePartialContent();
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) {
try {
if (load.length == 0) {
(new ProtocolP2PDatagram(new Payload(RequestResponseCode.EMPTY_FILE))).send(socket, pd);
} 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) {
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) {
@ -141,6 +159,7 @@ public class ServerManagementUDP implements Runnable {
}
fileList = new String[v.size()];
v.toArray(fileList);
Arrays.sort(fileList);
}