parent
868d657317
commit
d01bbd85f2
@ -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>]
|
||||
```
|
||||
|
||||
|
@ -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 server’s directory content
|
||||
|
@ -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 filename’s 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 filename’s size from bytes 24 to 27 of datagram.
|
||||
/** Read filename’s size from datagram.
|
||||
* @param datagram received datagram
|
||||
* @throws ProtocolError
|
||||
* @throws SizeError
|
||||
* @return filename’s 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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user