Projet_JAVA_P2P_STRI2A/src/protocolP2P/ProtocolP2PPacketUDP.java
2020-03-03 16:39:19 +01:00

362 lines
13 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package protocolP2P;
import exception.InternalError;
import exception.ProtocolError;
import exception.SizeError;
import exception.TransmissionError;
import exception.VersionError;
import exception.SocketClosed;
import remoteException.EmptyDirectory;
import remoteException.InternalRemoteError;
import remoteException.NotFound;
import remoteException.ProtocolRemoteError;
import remoteException.VersionRemoteError;
import remoteException.EmptyFile;
import tools.BytesArrayTools;
import protocolP2P.Payload;
import protocolP2P.RequestResponseCode;
import protocolP2P.LoadRequest;
import protocolP2P.FileList;
import protocolP2P.FilePart;
import java.util.ArrayList;
import java.lang.Byte;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketAddress;
import java.io.IOException;
/** Representation of packet.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class ProtocolP2PPacketUDP extends ProtocolP2PPacket {
private final static int CHECKSUM_POSITION = 2;
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(Payload 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[] packet = new byte[1024];
DatagramPacket reception = new DatagramPacket(packet, packet.length);
ss.receive(reception);
responseSocketAddress = reception.getSocketAddress();
// 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:
// we were expecting a request, but we are receiving a response
throw new ProtocolError();
default :
break;
}
} catch (TransmissionError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss, responseSocketAddress);
throw e;
} catch (ProtocolError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(ss, responseSocketAddress);
throw e;
} catch (VersionError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.VERSION_ERROR))).send(ss, responseSocketAddress);
throw e;
} catch (InternalError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss, responseSocketAddress);
throw e;
} catch (SizeError e) {
(new ProtocolP2PPacketUDP(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 void sendResponse(ProtocolP2PPacket 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 EmptyDirectory
* @throws InternalRemoteError
* @throws VersionRemoteError
* @throws ProtocolRemoteError
* @throws TransmissionError
* @throws ProtocolError
* @throws VersionError
* @throws InternalError
* @throws SizeError
* @throws IOException
*/
public ProtocolP2PPacket receiveResponse() throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException {
assert requestSocket != null : "Cannot receive response because request packet not sent.";
if (requestSocket == null) {
throw new InternalError();
}
// reception
byte[] packet = new byte[8192];
DatagramPacket reception = new DatagramPacket(packet, packet.length);
requestSocket.receive(reception);
// 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();
default :
return (ProtocolP2PPacket)p;
}
} catch (TransmissionError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e;
} catch (ProtocolError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(requestSocket);
throw e;
} catch (VersionError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.VERSION_ERROR))).send(requestSocket);
throw e;
} catch (InternalError e) {
(new ProtocolP2PPacketUDP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e;
} catch (SizeError e) {
(new ProtocolP2PPacketUDP(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;
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();
}
}
}