Projet_JAVA_P2P_STRI2A/src/protocolP2P/ProtocolP2PPacketTCP.java
2020-03-03 19:30:04 +01:00

316 lines
10 KiB
Java

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.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
/** Representation of packet.
* @author Louis Royer
* @author Flavien Haas
* @author JS Auge
* @version 1.0
*/
public class ProtocolP2PPacketTCP extends ProtocolP2PPacket {
private Socket responseSocket; // socket used to recept request and send response
private Socket 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 ProtocolP2PPacketTCP(Payload payload) {
super(payload);
}
/** Send a Packet. Socket must be set and connected.
* @param socket Socket used to send Packet.
* @throws InternalError
* @throws SocketClosed
* @throws IOException
*/
protected void send(Socket socket) throws InternalError, SocketClosed, IOException {
assert socket != null : "Trying to send a Packet but no socket defined";
assert socket.isConnected() : "Trying to send a Packet but socket not connected";
if (socket == null || (!socket.isConnected())) {
throw new InternalError();
}
// generate Packet
byte[] packet = toPacket();
// send it
try {
OutputStream outputStream = socket.getOutputStream();
outputStream.write(packet);
outputStream.flush();
} catch (IOException e) {
// Error: cannot send response, closing socket
try {
socket.close();
} catch (IOException e2) {
System.err.println("Cannot close socket");
} finally {
throw new SocketClosed();
}
}
}
/** Send a Request throught socket. Socket must be connected (typically used from client).
* @param socket Socket. Must be connected.
* @throws InternalError
* @throws SocketClosed
* @throws IOException
*/
public void sendRequest(Object socket) throws InternalError, IOException, SocketClosed {
assert socket instanceof Socket: "Wrong socket type";
if (socket instanceof Socket) {
requestSocket = (Socket)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 SocketClosed
* @throws IOException
* @return ProtocolP2PPacket received.
*/
public ProtocolP2PPacketTCP(Object socket) throws TransmissionError, ProtocolError, VersionError, InternalError, SizeError, SocketClosed, IOException {
super(socket);
assert socket instanceof Socket : "Wrong socket type";
if (!(socket instanceof Socket)) {
throw new InternalError();
}
Socket ss = (Socket)socket;
byte[] packet = new byte[1024];
try {
System.err.println("Reading " + ss.getInputStream().read(packet) + " bytes");
} catch (IOException e) {
// Error: cannot read request, closing socket
try {
ss.close();
} catch (IOException e2) {
System.err.println("Cannot close socket");
} finally {
throw new SocketClosed();
}
}
// 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 ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss);
throw e;
} catch (ProtocolError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(ss);
throw e;
} catch (VersionError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.VERSION_ERROR))).send(ss);
throw e;
} catch (InternalError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss);
throw e;
} catch (SizeError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(ss);
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
* @throws SocketClosed
*/
public void sendResponse(ProtocolP2PPacket response) throws InternalError, IOException, SocketClosed {
assert response instanceof ProtocolP2PPacketTCP: "Wrong Packet type";
if (response instanceof ProtocolP2PPacketTCP) {
ProtocolP2PPacketTCP r = (ProtocolP2PPacketTCP) response;
assert responseSocket != null : "Cannot send response to a packet not received";
if (responseSocket == null) {
throw new InternalError();
}
r.send(responseSocket);
} 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
* @throws SocketClosed
*/
public ProtocolP2PPacket receiveResponse() throws EmptyFile, NotFound, EmptyDirectory, InternalRemoteError, VersionRemoteError, ProtocolRemoteError, TransmissionError, ProtocolError, VersionError, InternalError, SizeError, IOException, SocketClosed {
assert requestSocket != null : "Cannot receive response because request packet not sent.";
if (requestSocket == null) {
throw new InternalError();
}
// reception
byte[] packet = new byte[8192];
requestSocket.getInputStream().read(packet);
// contruction
try {
ProtocolP2PPacketTCP p = new ProtocolP2PPacketTCP(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 ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e;
} catch (ProtocolError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.PROTOCOL_ERROR))).send(requestSocket);
throw e;
} catch (VersionError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.VERSION_ERROR))).send(requestSocket);
throw e;
} catch (InternalError e) {
(new ProtocolP2PPacketTCP(new Payload(RequestResponseCode.INTERNAL_ERROR))).send(requestSocket);
throw e;
} catch (SizeError e) {
(new ProtocolP2PPacketTCP(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 ProtocolP2PPacketTCP(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
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, Socket 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;
return packet;
}
}