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; } }