You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Projet_JAVA_P2P_STRI2A/src/protocolP2P/ProtocolP2PPacketUDP.java

435 lines
15 KiB
Java

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

package protocolP2P;
import localException.InternalError;
import localException.ProtocolError;
import localException.SizeError;
import localException.TransmissionError;
import localException.VersionError;
import localException.SocketClosed;
import remoteException.NotATracker;
import remoteException.EmptyDirectory;
import remoteException.InternalRemoteError;
import remoteException.NotFound;
import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.EmptyFile;
import remoteException.UnknownHost;
import tools.BytesArrayTools;
import tools.HostItem;
import protocolP2P.Payload;
import protocolP2P.RequestResponseCode;
import protocolP2P.LoadRequest;
import protocolP2P.FileList;
import protocolP2P.FilePart;
import protocolP2P.RatioRequest;
import protocolP2P.RatioResponse;
import protocolP2P.UpdateRatio;
import protocolP2P.Denied;
import protocolP2P.SizeRequest;
import protocolP2P.SizeResponse;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.io.IOException;
import java.util.Arrays;
/** Representation of packet.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class ProtocolP2PPacketUDP < T extends Payload> extends ProtocolP2PPacket < T > {
private final static int CHECKSUM_POSITION = 2;
private HostItem remoteHost;
private SocketAddress responseSocketAddress; // socket address used when receptionning request and to sending response
private DatagramSocket responseSocket; // socket used to recept request and send response
private DatagramSocket requestSocket; // socket used to send request and to reception response
/** Constructor with payload parameter (typically used when sending packet).
* @param payload the payload associated with the packet to send
*/
public ProtocolP2PPacketUDP(T payload) {
super(payload);
}
/** Send a Packet. Socket must be set and connected.
* @param socket DatagramSocket used to send Packet.
* @throws InternalError
* @throws IOException
*/
protected void send(DatagramSocket socket) throws InternalError, IOException {
send(socket, null);
}
/** Send a Packet. Socket must be set and connected, or an addr can be given.
* @param socket DatagramSocket used to send Packet.
* @param addr SocketAddress used to send Packet.
* @throws InternalError
* @throws IOException
*/
protected void send(DatagramSocket socket, SocketAddress addr) throws InternalError, IOException {
assert socket != null : "Trying to send a Packet but no socket defined";
assert socket.isConnected() || addr != null : "Trying to send a Packet but socket not connected, and no socketAddress given";
if (socket == null || ((!socket.isConnected()) && (addr == null))) {
throw new InternalError();
}
// generate DatagramPacket
byte[] packet = toPacket();
DatagramPacket datagramPacket = new DatagramPacket(packet, packet.length);
if (addr != null) {
datagramPacket.setSocketAddress(addr);
}
// send it
socket.send(datagramPacket);
}
/** Send a Request throught socket. Socket must be connected (typically used from client).
* @param socket DatagramSocket. Must be connected.
* @throws InternalError
* @throws IOException
*/
public void sendRequest(Object socket) throws InternalError, IOException {
assert socket instanceof DatagramSocket: "Wrong socket type";
if (socket instanceof DatagramSocket) {
requestSocket = (DatagramSocket)socket;
send(requestSocket);
} else {
throw new InternalError();
}
}
/** Receive Request (typically used from server).
* @param socket socket used to receive request
* @throws TransmissionError
* @throws ProtocolError
* @throws VersionError
* @throws InternalError
* @throws SizeError
* @throws IOException
* @throws SocketClosed
* @return ProtocolP2PPacket received.
*/
public ProtocolP2PPacketUDP(Object socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, SocketClosed {
super(socket);
assert socket instanceof DatagramSocket : "Wrong socket type";
if (!(socket instanceof DatagramSocket)) {
throw new InternalError();
}
DatagramSocket ss = (DatagramSocket)socket;
byte[] packetTmp = new byte[0xFFFF];
DatagramPacket reception = new DatagramPacket(packetTmp, packetTmp.length);
ss.receive(reception);
int payloadSize = Payload.getPayloadSize(packetTmp);
byte[] packet = Arrays.copyOf(packetTmp, Payload.PAYLOAD_START_POSITION + payloadSize);
responseSocketAddress = reception.getSocketAddress();
remoteHost = new HostItem(reception.getAddress().getHostName(), reception.getPort());
// contruction
boolean protocolError = false;
try {
constructPacket(packet, ss);
Payload payload = getPayload();
switch (payload.getRequestResponseCode()) {
case PROTOCOL_ERROR :
// we do not want to create an infinite loop of protocolError message exchange.
protocolError = true;
break;
case VERSION_ERROR :
case INTERNAL_ERROR :
case EMPTY_DIRECTORY :
case NOT_FOUND :
case EMPTY_FILE:
case LOAD_RESPONSE:
case LIST_RESPONSE:
case HASH_RESPONSE:
case DISCOVER_RESPONSE:
case NOT_A_TRACKER:
case RATIO_RESPONSE:
case DENIED:
case SIZE_RESPONSE:
// we were expecting a request, but we are receiving a response
throw new ProtocolError();
default :
break;
}
} catch (TransmissionError e) {
(new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss, responseSocketAddress);
throw e;
} catch (ProtocolError e) {
(new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(ss, responseSocketAddress);
throw e;
} catch (VersionError e) {
(new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.VERSION_ERROR))).send(ss, responseSocketAddress);
throw e;
} catch (InternalError e) {
(new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss, responseSocketAddress);
throw e;
} catch (SizeError e) {
(new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss, responseSocketAddress);
throw e;
}
if (protocolError) {
throw new ProtocolError();
}
}
/** Send a Response to a Request (typically used from server).
* @param response Packet to send as a response.
* @throws InternalError
* @throws IOException
*/
public <U extends ProtocolP2PPacket<?>>void sendResponse(U response) throws InternalError, IOException {
assert response instanceof ProtocolP2PPacketUDP: "Wrong Packet type";
if (response instanceof ProtocolP2PPacketUDP) {
ProtocolP2PPacketUDP<?> r = (ProtocolP2PPacketUDP<?>) response;
assert responseSocket != null : "Cannot send response to a packet not received";
if (responseSocket == null) {
throw new InternalError();
}
assert responseSocketAddress != null : "Cannot send response because address of client has not been saved";
if (responseSocketAddress == null) {
throw new InternalError();
}
r.send(responseSocket, responseSocketAddress);
} else {
throw new InternalError();
}
}
/** Receive response (typically used by client).
* @return ProtocolP2PPacket received
* @throws EmptyFile
* @throws NotFound
* @throws NotATracker
* @throws EmptyDirectory
* @throws InternalRemoteError
* @throws VersionRemoteError
* @throws ProtocolRemoteError
* @throws TransmissionError
* @throws ProtocolError
* @throws VersionError
* @throws InternalError
* @throws SizeError
* @throws IOException
* @throws UnknownHost
*/
public ProtocolP2PPacket<?> receiveResponse() throws EmptyFile, NotFound, NotATracker, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, UnknownHost {
assert requestSocket != null : "Cannot receive response because request packet not sent.";
if (requestSocket == null) {
throw new InternalError();
}
// reception
byte[] packetTmp = new byte[0xFFFF];
DatagramPacket reception = new DatagramPacket(packetTmp, packetTmp.length);
requestSocket.receive(reception);
int payloadSize = Payload.getPayloadSize(packetTmp);
byte[] packet = Arrays.copyOf(packetTmp, Payload.PAYLOAD_START_POSITION + payloadSize);
// contruction
try {
ProtocolP2PPacketUDP<?> p = new ProtocolP2PPacketUDP<>(packet);
Payload payload = p.getPayload();
switch (payload.getRequestResponseCode()) {
case PROTOCOL_ERROR :
throw new ProtocolRemoteError();
case VERSION_ERROR :
throw new VersionRemoteError();
case INTERNAL_ERROR :
throw new InternalRemoteError();
case EMPTY_DIRECTORY :
throw new EmptyDirectory();
case NOT_FOUND :
throw new NotFound();
case EMPTY_FILE:
throw new EmptyFile();
case NOT_A_TRACKER:
throw new NotATracker();
case UNKNOWN_HOST:
throw new UnknownHost();
default :
return (ProtocolP2PPacket)p;
}
} catch (TransmissionError e) {
(new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e;
} catch (ProtocolError e) {
(new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(requestSocket);
throw e;
} catch (VersionError e) {
(new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.VERSION_ERROR))).send(requestSocket);
throw e;
} catch (InternalError e) {
(new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e;
} catch (SizeError e) {
(new ProtocolP2PPacketUDP<Payload>(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e;
}
}
/** Private constructor with packet as byte[] parameter (typically used when receiving Packet response).
* @param packet the full Packet received
* @throws TransmissionError
* @throws ProtocolError
* @throws VersionError
* @throws InternalError
* @throws SizeError
*/
private ProtocolP2PPacketUDP(byte[] packet) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError {
super(packet);
constructPacket(packet);
}
/** Private constructor helper with packet as byte[] parameter (typically used when receiving Packet response/request).
* @param packet the full Packet received
* @throws TransmissionError
* @throws ProtocolError
* @throws VersionError
* @throws InternalError
* @throws SizeError
*/
private void constructPacket(byte[] packet) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError {
// unwrap version
version = packet[VERSION_POSITION];
checkProtocolVersion(); // this can throw VersionError
checkCheckSum(packet);
RequestResponseCode r = RequestResponseCode.fromCode(packet[RequestResponseCode.RRCODE_POSITION]); // this can throw ProtocolError
switch (r) {
case LIST_RESPONSE:
payload = (Payload) new FileList(packet);
break;
case LOAD_RESPONSE:
payload = (Payload) new FilePart(packet);
break;
case LOAD_REQUEST:
payload = (Payload) new LoadRequest(packet);
break;
case HASH_REQUEST:
payload = (Payload) new HashRequest(packet);
break;
case HASH_RESPONSE:
payload = (Payload) new HashResponse(packet);
break;
case REGISTER:
payload = (Payload) new Register(packet);
break;
case UNREGISTER:
payload = (Payload) new Unregister(packet);
break;
case DISCOVER_REQUEST:
payload = (Payload) new DiscoverRequest(packet);
break;
case DISCOVER_RESPONSE:
payload = (Payload) new DiscoverResponse(packet);
break;
case RATIO_REQUEST:
payload = (Payload) new RatioRequest(packet);
break;
case RATIO_RESPONSE:
payload = (Payload) new RatioResponse(packet);
break;
case UPDATE_RATIO:
payload = (Payload) new UpdateRatio(packet);
break;
case DENIED:
payload = (Payload) new Denied(packet);
break;
case SIZE_REQUEST:
payload = (Payload) new SizeRequest(packet);
break;
case SIZE_RESPONSE:
payload = (Payload) new SizeResponse(packet);
break;
default:
payload = new Payload(packet); // this can throw TransmissionError
break;
}
}
/** Private constructor helper with packet as byte[] parameter and (typically used when receiving Packet request).
* @param packet the full Packet received
* @param responseSocket socket address used to reception this request (use this one to respond)
* @throws TransmissionError
* @throws ProtocolError
* @throws VersionError
* @throws InternalError
* @throws SizeError
*/
private void constructPacket(byte[] packet, DatagramSocket responseSocket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError {
constructPacket(packet);
this.responseSocket = responseSocket;
}
/** Returns a byte[] containing full packet (typically used when sending packet).
* This packet is complete and ready to be send.
* @return the full packet to send
* @throws InternalError
*/
protected byte[] toPacket() throws InternalError {
byte[] packet = payload.toPacket();
packet[VERSION_POSITION] = version;
setCheckSum(packet);
return packet;
}
/** Compute checksum associated to packet.
* @param packet the full packet received
* @throws SizeError
*/
private int computeCheckSum(byte [] packet) throws SizeError {
/*
* The checksum field is the 16 bit ones complement of the ones complement sum of all 16-bit words
* in the header and text. If a segment contains an odd number of header and text octets to be checksummed,
* the last octet is padded on the right with zeros to form a 16-bit word for checksum purposes.
* The pad is not transmitted as part of the segment. While computing the checksum, the checksum field
* itself is replaced with zeros.
*/
int checksum = 0;
for (int i=CHECKSUM_POSITION+2; i<CHECKSUM_POSITION+2+4+Payload.getPayloadSize(packet); ++i) {
checksum += BytesArrayTools.readInt16Bits(packet, i);
checksum &= 0xffff;
}
return checksum ^ 0xffff;
}
/** Used to set checksum into packet
* @param packet full packet
* @throws InternalError
*/
private void setCheckSum(byte [] packet) throws InternalError {
try {
int checksum = computeCheckSum(packet);
BytesArrayTools.write16Bits(packet, CHECKSUM_POSITION, checksum);
} catch (SizeError e) {
throw new InternalError();
}
}
/** Used to check if the checksum is correct
* @param packet full packet
* @throws TransmissionError
*/
private void checkCheckSum(byte [] packet) throws TransmissionError {
try {
int checksum = BytesArrayTools.readInt16Bits(packet, CHECKSUM_POSITION);
int computedCheckSum = computeCheckSum(packet);
if (computedCheckSum != checksum){
throw new TransmissionError();
}
} catch(SizeError e) {
throw new TransmissionError();
}
}
/** Get hostItem of the sender
* @return hostItem of the sender
* @throws InternalError
*/
public HostItem getHostItem() throws InternalError {
if (remoteHost == null) {
throw new InternalError();
}
return remoteHost;
}
}