Add protocol elements for tracker

Needs to implements tracker now.
This commit is contained in:
Louis Royer 2020-03-19 17:31:03 +01:00
parent f1beed30e1
commit 5c89bc6957
14 changed files with 334 additions and 17 deletions

View File

@ -8,6 +8,7 @@ package protocolP2P;
*/
public enum CodeType {
REQUEST,
REQUEST_TRACKER,
RESPONSE,
ERROR
}

View File

@ -0,0 +1,65 @@
package protocolP2P;
import protocolP2P.Payload;
import tools.BytesArrayTools;
import localException.InternalError;
import localException.SizeError;
import localException.ProtocolError;
import localException.TransmissionError;
public class DiscoverRequest extends Payload {
private String filename;
/** Constructor with filename (typically used by client). If filename is null, it is initialized with "".
* @param filename Name of the file you want a server list of.
* @throws InternalError
*/
public DiscoverRequest(String filename) throws InternalError {
super(RequestResponseCode.DISCOVER_REQUEST);
if (filename == null) {
this.filename = "";
} else {
this.filename = filename;
}
}
/** Constructor (typically used by server) with a byte[] parameter containing the Packet received.
* @param packet the full Packet received
* @throws SizeError
* @throws InternalError
* @throws ProtocolError
* @throws TransmissionError
*/
protected DiscoverRequest(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
super(packet);
int size = getPayloadSize(packet);
filename = BytesArrayTools.readString(packet, Payload.PAYLOAD_START_POSITION, size);
}
/** Returns a byte[] containing Packet with padding.
* This Packet is still incomplete and should not be send directly.
* ProtocolP2PPacket will use this method to generate the complete Packet.
* @return Packet with padding
* @throws InternalError
*/
protected byte[] toPacket() throws InternalError {
// compute total size
int size = PAYLOAD_START_POSITION + filename.length();
byte[] packet = new byte[size + 1]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size
setPayloadSize(size - PAYLOAD_START_POSITION, packet);
// write filename to Packet
BytesArrayTools.write(packet, filename, PAYLOAD_START_POSITION);
return packet;
}
/** Filename getter.
* @return filename
*/
public String getFilename() {
return filename;
}
}

View File

@ -0,0 +1,111 @@
package protocolP2P;
import protocolP2P.Payload;
import tools.HostItem;
import java.util.ArrayList;
import java.util.List;
import localException.InternalError;
import localException.SizeError;
import localException.ProtocolError;
import localException.TransmissionError;
import tools.BytesArrayTools;
public class DiscoverResponse extends Payload {
private List<HostItem> hostList;
private String filename;
private static final int FILENAME_SIZE_POSITION = PAYLOAD_START_POSITION;
private static final int FILENAME_POSITION = FILENAME_SIZE_POSITION + 4;
/** Constructor with filename (typically used by tracker). If filename is null, it is initialized with "".
* @param filename Name of the file related to the server list.
* @param hostList List of servers
* @throws InternalError
*/
public DiscoverResponse(String filename, List<HostItem> hostList) throws InternalError {
super(RequestResponseCode.DISCOVER_RESPONSE);
this.filename = filename;
this.hostList = hostList;
}
/** Constructor (typically used by server) with a byte[] parameter containing the Packet received.
* @param packet the full Packet received
* @throws SizeError
* @throws InternalError
* @throws ProtocolError
* @throws TransmissionError
*/
protected DiscoverResponse(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
super(packet);
int size = getPayloadSize(packet);
/* Read filename size */
int filenameSize = BytesArrayTools.readInt(packet, FILENAME_SIZE_POSITION);
/* Read filename */
filename = BytesArrayTools.readString(packet, FILENAME_POSITION, filenameSize);
// TODO
hostList = new ArrayList<>();
int i = FILENAME_POSITION + filenameSize;
while(i<size) {
int port = BytesArrayTools.readInt16Bits(packet, i);
i += 2;
String hostname = BytesArrayTools.readString(packet, i, "\n");
i += hostname.length();
hostList.add(new HostItem(hostname, port));
}
}
/** Returns a byte[] containing Packet with padding.
* This Packet is still incomplete and should not be send directly.
* ProtocolP2PPacket will use this method to generate the complete Packet.
* @return Packet with padding
* @throws InternalError
*/
protected byte[] toPacket() throws InternalError {
int filenameSize = filename.length();
int hostListSize = 0;
for (HostItem hostItem: hostList) {
hostListSize += (2 + hostItem.getHostname().length() + 1);
}
// compute total size
int size = FILENAME_POSITION + filename.length() + hostListSize;
byte[] packet = new byte[size + 1]; // java initialize all to zero
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size
setPayloadSize(size - PAYLOAD_START_POSITION, packet);
// write filename size
BytesArrayTools.write(packet, FILENAME_SIZE_POSITION, filenameSize);
// write filename
BytesArrayTools.write(packet, filename, FILENAME_POSITION);
int i = FILENAME_POSITION + filename.length();
// write hostList
for(HostItem hostItem: hostList) {
try {
BytesArrayTools.write16Bits(packet, i, hostItem.getPort());
i+=2;
String hostname = hostItem.getHostname() + "\n";
BytesArrayTools.write(packet, hostname, i);
i+=hostname.length();
} catch (SizeError e) {
throw new InternalError();
}
}
return packet;
}
/** HostList getter.
* @return hostList
*/
public List<HostItem> getHostList() {
return hostList;
}
/** Filename getter.
* @return filename
*/
public String getFilename() {
return filename;
}
}

View File

@ -83,7 +83,7 @@ public class FilePart extends Payload {
// set request/response code
packet[RequestResponseCode.RRCODE_POSITION] = requestResponseCode.codeValue;
// set Payload size
setPayloadSize(size - OFFSET_POSITION, packet);
setPayloadSize(size - PAYLOAD_START_POSITION, packet);
// write offset to Packet
BytesArrayTools.write(packet, OFFSET_POSITION, offset);
// write totalSize to Packet

View File

@ -5,6 +5,8 @@ import protocolP2P.FileList;
import protocolP2P.LoadRequest;
import protocolP2P.HashRequest;
import protocolP2P.HashResponse;
import protocolP2P.DiscoverRequest;
import protocolP2P.DiscoverResponse;
import localException.ProtocolError;
import localException.InternalError;
import localException.TransmissionError;
@ -27,11 +29,13 @@ public class Payload {
*/
public Payload(RequestResponseCode requestResponseCode) throws InternalError {
/* asserts to help debugging */
assert requestResponseCode != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class";
assert requestResponseCode != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class";
assert requestResponseCode != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class";
assert requestResponseCode != RequestResponseCode.HASH_REQUEST || (this instanceof HashRequest) : "HASH_REQUEST must use HashRequest class";
assert requestResponseCode != RequestResponseCode.HASH_RESPONSE || (this instanceof HashResponse) : "HASH_RESPONSE must use HashResponse class";
assert requestResponseCode != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class";
assert requestResponseCode != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class";
assert requestResponseCode != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class";
assert requestResponseCode != RequestResponseCode.HASH_REQUEST || (this instanceof HashRequest) : "HASH_REQUEST must use HashRequest class";
assert requestResponseCode != RequestResponseCode.HASH_RESPONSE || (this instanceof HashResponse) : "HASH_RESPONSE must use HashResponse class";
assert requestResponseCode != RequestResponseCode.DISCOVER_REQUEST || (this instanceof DiscoverRequest) : "DISCOVER_REQUEST must use DiscoverRequest class";
assert requestResponseCode != RequestResponseCode.DISCOVER_RESPONSE || (this instanceof DiscoverResponse) : "DISCOVER_RESPONSE must use DiscoverResponse class";
this.requestResponseCode = requestResponseCode;
checkRequestResponseCode(); // this can throw InternalError
}
@ -46,13 +50,17 @@ public class Payload {
*/
protected Payload(byte[] packet) throws SizeError, ProtocolError, InternalError, TransmissionError {
/* asserts to help debugging */
assert getPayloadSize(packet) + 8 <= packet.length : "Payload is truncated";
if (packet.length < getPayloadSize(packet) + 8) {
assert getPayloadSize(packet) + PAYLOAD_START_POSITION <= packet.length : "Payload is truncated";
if (packet.length < getPayloadSize(packet) + PAYLOAD_START_POSITION) {
throw new TransmissionError();
}
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LIST_RESPONSE || (this instanceof FileList) : "LIST_RESPONSE must use FilePart class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_RESPONSE || (this instanceof FilePart) : "LOAD_RESPONSE must use FileList class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.LOAD_REQUEST || (this instanceof LoadRequest) : "LOAD_REQUEST must use LoadRequest class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.HASH_REQUEST || (this instanceof HashRequest) : "HASH_REQUEST must use HashRequest class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.HASH_RESPONSE || (this instanceof HashResponse) : "HASH_RESPONSE must use HashResponse class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.DISCOVER_REQUEST || (this instanceof DiscoverRequest) : "DISCOVER_REQUEST must use DiscoverRequest class";
assert RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]) != RequestResponseCode.DISCOVER_RESPONSE || (this instanceof DiscoverResponse) : "DISCOVER_RESPONSE must use DiscoverResponse class";
requestResponseCode = RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]);
checkRequestResponseCode(); // this can throw InternalError
}
@ -62,9 +70,14 @@ public class Payload {
*/
private void checkRequestResponseCode() throws InternalError {
/* Incorrect use cases (use subclasses instead) */
if ((requestResponseCode == RequestResponseCode.LIST_RESPONSE && !(this instanceof FileList))
|| (requestResponseCode == RequestResponseCode.LOAD_RESPONSE && !(this instanceof FilePart))
|| (requestResponseCode == RequestResponseCode.LOAD_REQUEST && !(this instanceof LoadRequest))) {
if ((requestResponseCode == RequestResponseCode.LIST_RESPONSE && !(this instanceof FileList))
|| (requestResponseCode == RequestResponseCode.LOAD_RESPONSE && !(this instanceof FilePart))
|| (requestResponseCode == RequestResponseCode.LOAD_REQUEST && !(this instanceof LoadRequest))
|| (requestResponseCode == RequestResponseCode.HASH_REQUEST && !(this instanceof HashRequest))
|| (requestResponseCode == RequestResponseCode.HASH_RESPONSE && !(this instanceof HashResponse))
|| (requestResponseCode == RequestResponseCode.DISCOVER_REQUEST && !(this instanceof DiscoverRequest))
|| (requestResponseCode == RequestResponseCode.DISCOVER_RESPONSE && !(this instanceof DiscoverResponse))
) {
throw new InternalError();
}
}

View File

@ -13,6 +13,7 @@ import remoteException.VersionRemoteError;
import remoteException.EmptyFile;
import java.net.InetAddress;
import java.io.IOException;
import tools.HostItem;
/** Representation of packet.
* @author Louis Royer
@ -50,6 +51,12 @@ public abstract class ProtocolP2PPacket {
*/
public abstract void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException, SocketClosed;
/** Get hostItem of the sender
* @return hostItem of the sender
* @throws InternalError
*/
public abstract HostItem getHostItem() throws InternalError;
/** Receive a response
* @throws EmptyFile
* @throws NotFound

View File

@ -12,6 +12,7 @@ import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.EmptyFile;
import tools.BytesArrayTools;
import tools.HostItem;
import protocolP2P.Payload;
import protocolP2P.RequestResponseCode;
import protocolP2P.LoadRequest;
@ -319,4 +320,15 @@ public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
return packet;
}
/** Get hostItem of the sender
* @return hostItem of the sender
* @throws InternalError
*/
public HostItem getHostItem() throws InternalError {
if (responseSocket == null) {
throw new InternalError();
}
return new HostItem(responseSocket.getInetAddress().getHostName(), responseSocket.getPort());
}
}

View File

@ -12,6 +12,7 @@ import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.EmptyFile;
import tools.BytesArrayTools;
import tools.HostItem;
import protocolP2P.Payload;
import protocolP2P.RequestResponseCode;
import protocolP2P.LoadRequest;
@ -353,7 +354,6 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
* @param packet full packet
* @throws TransmissionError
*/
private void checkCheckSum(byte [] packet) throws TransmissionError {
try {
int checksum = BytesArrayTools.readInt16Bits(packet, CHECKSUM_POSITION);
@ -365,4 +365,15 @@ public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
throw new TransmissionError();
}
}
/** Get hostItem of the sender
* @return hostItem of the sender
* @throws InternalError
*/
public HostItem getHostItem() throws InternalError {
if (responseSocket == null) {
throw new InternalError();
}
return new HostItem(responseSocket.getInetAddress().getHostName(), responseSocket.getPort());
}
}

View File

@ -15,15 +15,20 @@ public enum RequestResponseCode {
LIST_REQUEST(CodeType.REQUEST, (byte)0x00),
LOAD_REQUEST(CodeType.REQUEST, (byte)0x01),
HASH_REQUEST(CodeType.REQUEST, (byte)0x02),
DISCOVER_REQUEST(CodeType.REQUEST_TRACKER, (byte)0x03),
REGISTER(CodeType.REQUEST_TRACKER, (byte)0x04),
UNREGISTER(CodeType.REQUEST_TRACKER, (byte)0x05),
LIST_RESPONSE(CodeType.RESPONSE, (byte)0x80),
LOAD_RESPONSE(CodeType.RESPONSE, (byte)0x81),
HASH_RESPONSE(CodeType.RESPONSE, (byte)0x82),
DISCOVER_RESPONSE(CodeType.RESPONSE, (byte)0x83),
VERSION_ERROR(CodeType.ERROR, (byte)0xC0),
PROTOCOL_ERROR(CodeType.ERROR, (byte)0xC1),
INTERNAL_ERROR(CodeType.ERROR, (byte)0xC2),
EMPTY_DIRECTORY(CodeType.ERROR, (byte)0xC3),
NOT_FOUND(CodeType.ERROR, (byte)0xC4),
EMPTY_FILE(CodeType.ERROR, (byte)0xC5);
EMPTY_FILE(CodeType.ERROR, (byte)0xC5),
NOT_A_TRACKER(CodeType.ERROR, (byte)0xC6);
public final CodeType codeType;
public final byte codeValue;

View File

@ -0,0 +1,7 @@
package remoteException;
import exception.RemoteException;
public class NotATracker extends exception.RemoteException {
private static final long serialVersionUID = 12L;
}

View File

@ -136,6 +136,18 @@ public class ServerManagementTCP implements Runnable {
logger.writeTCP(addr + "HASH_REQUEST", LogLevel.Action);
sendHashResponse(pd);
break;
case DISCOVER_REQUEST:
logger.writeTCP(addr + "DISCOVER_REQUEST", LogLevel.Action);
sendNotATracker(pd);
break;
case UNREGISTER:
logger.writeTCP(addr + "UNREGISTER", LogLevel.Action);
sendNotATracker(pd);
break;
case REGISTER:
logger.writeTCP(addr + "REGISTER", LogLevel.Action);
sendNotATracker(pd);
break;
default:
logger.writeTCP(addr + "Received grabbage", LogLevel.Action);
sendInternalError(pd);
@ -198,7 +210,18 @@ public class ServerManagementTCP implements Runnable {
logger.writeTCP(e, LogLevel.Error);
}
}
/** Send a NotATracker error message.
* @param pd ProtocolP2PPacketTCP to respond
*/
private void sendNotATracker(ProtocolP2PPacketTCP pd) {
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.NOT_A_TRACKER)));
} catch (Exception e) {
logger.writeTCP(e, LogLevel.Error);
}
}
/** Send response to list request
* @param pd Request received
*/

View File

@ -89,6 +89,12 @@ public class ServerManagementUDP implements Runnable {
logger.writeUDP("Received HASH_REQUEST", LogLevel.Action);
sendHashResponse(pd);
break;
case DISCOVER_REQUEST:
case UNREGISTER:
case REGISTER:
logger.writeUDP("Received Tracker request", LogLevel.Action);
sendNotATracker(pd);
break;
default:
sendInternalError(pd);
}
@ -213,6 +219,17 @@ public class ServerManagementUDP implements Runnable {
}
}
/** Send a NotATracker error message.
* @param pd ProtocolP2PPacketUDP to respond
*/
private void sendNotATracker(ProtocolP2PPacketUDP pd) {
try {
pd.sendResponse((ProtocolP2PPacket)new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.NOT_A_TRACKER)));
} catch (Exception e) {
logger.writeUDP(e, LogLevel.Error);
}
}
/** Send hash response to hash request
* @param pd Request received
*/

View File

@ -219,6 +219,37 @@ public class BytesArrayTools {
}
}
/** Read string from byte array
* @param array Byte array to read
* @param start start position in byte array
* @param endStr End string delimiter
* @return String read
* @throws InternalError
*/
public static String readString(byte[] array, int start, String endStr) throws InternalError {
boolean failed = false;
try {
int i = start;
while(true) {
for(byte b: endStr.getBytes()) {
if (b != array[i]) {
failed = true;
break;
}
}
if (failed) {
i++;
failed = false;
} else {
break;
}
}
return readString(array, start, i -1 - start);
} catch(IndexOutOfBoundsException e) {
throw new InternalError();
}
}
/** Write byte Array to byte Array.
* @param dst Destination byte Array
* @param src Source byte Array

View File

@ -79,10 +79,24 @@ public class HostItem {
}
return udpSocket;
}
/** Closes udp socket
*/
public void closeUDPSocket() {
if (udpSocket != null) {
udpSocket.close();
}
udpSocket = null;
}
/** Getter for hostname */
public String getHostname() {
return hostname;
}
/** Getter for port */
public int getPort() {
return port;
}
}