diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/.idea/Teaching-HEIGVD-RES-2021-Exercise-Protocol-Design.iml b/.idea/Teaching-HEIGVD-RES-2021-Exercise-Protocol-Design.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/Teaching-HEIGVD-RES-2021-Exercise-Protocol-Design.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..1763e15 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b3f665c --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/client/DumbHttpClient.iml b/client/DumbHttpClient.iml new file mode 100644 index 0000000..b872d82 --- /dev/null +++ b/client/DumbHttpClient.iml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/client/pom.xml b/client/pom.xml new file mode 100644 index 0000000..c35b865 --- /dev/null +++ b/client/pom.xml @@ -0,0 +1,39 @@ + + + 4.0.0 + ch.heigvd.res.examples + DumbHttpClient + 1.0-SNAPSHOT + jar + + + + org.apache.maven.plugins + maven-shade-plugin + 2.3 + + + package + + shade + + + true + standalone + + + ch.heigvd.res.examples.DumbHttpClient + + + + + + + + + + UTF-8 + 1.7 + 1.7 + + \ No newline at end of file diff --git a/client/src/main/java/ch/heigvd/res/examples/DumbHttpClient.java b/client/src/main/java/ch/heigvd/res/examples/DumbHttpClient.java new file mode 100644 index 0000000..dceeb25 --- /dev/null +++ b/client/src/main/java/ch/heigvd/res/examples/DumbHttpClient.java @@ -0,0 +1,89 @@ +package ch.heigvd.res.examples; + +import java.io.*; +import java.net.Socket; +import java.util.Scanner; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This is not really an HTTP client, but rather a very simple program that + * establishes a TCP connection with a real HTTP server. Once connected, the + * client sends "garbage" to the server (the client does not send a proper + * HTTP request that the server would understand). The client then reads the + * response sent back by the server and logs it onto the console. + * + * @author Olivier Liechti + */ +public class DumbHttpClient { + + static final Logger LOG = Logger.getLogger(DumbHttpClient.class.getName()); + + final static int BUFFER_SIZE = 1024; + + /** + * This method does the whole processing + */ + public void sendRequest() { + Socket clientSocket = null; + OutputStream os = null; + InputStream is = null; + + try { + clientSocket = new Socket("localhost", 2323); + os = clientSocket.getOutputStream(); + is = clientSocket.getInputStream(); + + Scanner sc= new Scanner(System.in); + String userInput = ""; + + while(userInput != "QUIT") { + LOG.log(Level.INFO, "Start"); + ByteArrayOutputStream responseBuffer = new ByteArrayOutputStream(); + byte[] buffer = new byte[BUFFER_SIZE]; + int newBytes; + while ((newBytes = is.read(buffer)) != -1) { + responseBuffer.write(buffer, 0, newBytes); + } + + LOG.log(Level.INFO, "Response sent by the server: "); + LOG.log(Level.INFO, responseBuffer.toString()); + + userInput = sc.nextLine(); + + os.write(userInput.getBytes()); + } + } catch (IOException ex) { + LOG.log(Level.SEVERE, null, ex); + } finally { + try { + is.close(); + } catch (IOException ex) { + Logger.getLogger(DumbHttpClient.class.getName()).log(Level.SEVERE, null, ex); + } + try { + os.close(); + } catch (IOException ex) { + Logger.getLogger(DumbHttpClient.class.getName()).log(Level.SEVERE, null, ex); + } + try { + clientSocket.close(); + } catch (IOException ex) { + Logger.getLogger(DumbHttpClient.class.getName()).log(Level.SEVERE, null, ex); + } + } + LOG.log(Level.INFO, "QUIT"); + } + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + System.setProperty("java.util.logging.SimpleFormatter.format", "%5$s %n"); + + DumbHttpClient client = new DumbHttpClient(); + client.sendRequest(); + + } + +} diff --git a/my-app/.idea/.gitignore b/my-app/.idea/.gitignore new file mode 100644 index 0000000..73f69e0 --- /dev/null +++ b/my-app/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml +# Editor-based HTTP Client requests +/httpRequests/ diff --git a/my-app/.idea/compiler.xml b/my-app/.idea/compiler.xml new file mode 100644 index 0000000..2fdf4df --- /dev/null +++ b/my-app/.idea/compiler.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/my-app/.idea/encodings.xml b/my-app/.idea/encodings.xml new file mode 100644 index 0000000..63e9001 --- /dev/null +++ b/my-app/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/my-app/.idea/jarRepositories.xml b/my-app/.idea/jarRepositories.xml new file mode 100644 index 0000000..712ab9d --- /dev/null +++ b/my-app/.idea/jarRepositories.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/my-app/.idea/libraries/Maven__junit_junit_4_11.xml b/my-app/.idea/libraries/Maven__junit_junit_4_11.xml new file mode 100644 index 0000000..f33320d --- /dev/null +++ b/my-app/.idea/libraries/Maven__junit_junit_4_11.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/my-app/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml b/my-app/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml new file mode 100644 index 0000000..f58bbc1 --- /dev/null +++ b/my-app/.idea/libraries/Maven__org_hamcrest_hamcrest_core_1_3.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/my-app/.idea/misc.xml b/my-app/.idea/misc.xml new file mode 100644 index 0000000..972ec8d --- /dev/null +++ b/my-app/.idea/misc.xml @@ -0,0 +1,11 @@ + + + + + + + \ No newline at end of file diff --git a/my-app/.idea/modules.xml b/my-app/.idea/modules.xml new file mode 100644 index 0000000..a8cfcc2 --- /dev/null +++ b/my-app/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/my-app/.idea/vcs.xml b/my-app/.idea/vcs.xml new file mode 100644 index 0000000..6c0b863 --- /dev/null +++ b/my-app/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/my-app/my-app.iml b/my-app/my-app.iml new file mode 100644 index 0000000..3f4fd58 --- /dev/null +++ b/my-app/my-app.iml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/my-app/pom.xml b/my-app/pom.xml new file mode 100644 index 0000000..7e2d313 --- /dev/null +++ b/my-app/pom.xml @@ -0,0 +1,95 @@ + + + + 4.0.0 + + com.mycompany.app + my-app + 1.0-SNAPSHOT + + my-app + + http://www.example.com + + + UTF-8 + 1.7 + 1.7 + + + + + junit + junit + 4.11 + test + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + maven-resources-plugin + 3.0.2 + + + maven-compiler-plugin + 3.8.0 + + + maven-surefire-plugin + 2.22.1 + + + maven-jar-plugin + 3.0.2 + + + maven-install-plugin + 2.5.2 + + + maven-deploy-plugin + 2.8.2 + + + + maven-site-plugin + 3.7.1 + + + maven-project-info-reports-plugin + 3.0.0 + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.0 + + + + + + com.mycompany.app.TcpServers + + + + + + + + + + diff --git a/my-app/src/main/java/com/mycompany/app/App.java b/my-app/src/main/java/com/mycompany/app/App.java new file mode 100644 index 0000000..77cf3e0 --- /dev/null +++ b/my-app/src/main/java/com/mycompany/app/App.java @@ -0,0 +1,13 @@ +package com.mycompany.app; + +/** + * Hello world! + * + */ +public class App +{ + public static void main( String[] args ) + { + System.out.println( "Hello World!" ); + } +} diff --git a/my-app/src/main/java/com/mycompany/app/MultiThreadedServer.java b/my-app/src/main/java/com/mycompany/app/MultiThreadedServer.java new file mode 100644 index 0000000..29a1590 --- /dev/null +++ b/my-app/src/main/java/com/mycompany/app/MultiThreadedServer.java @@ -0,0 +1,174 @@ +package com.mycompany.app; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This class implements a multi-threaded TCP server. It is able to interact + * with several clients at the time, as well as to continue listening for + * connection requests. + * + * @author Olivier Liechti + */ +public class MultiThreadedServer { + + final static Logger LOG = Logger.getLogger(MultiThreadedServer.class.getName()); + + int port; + + /** + * Constructor + * + * @param port the port to listen on + */ + public MultiThreadedServer(int port) { + this.port = port; + } + + /** + * This method initiates the process. The server creates a socket and binds it + * to the previously specified port. It then waits for clients in a infinite + * loop. When a client arrives, the server will read its input line by line + * and send back the data converted to uppercase. This will continue until the + * client sends the "BYE" command. + */ + public void serveClients() { + LOG.info("Starting the Receptionist Worker on a new thread..."); + new Thread(new ReceptionistWorker()).start(); + } + + /** + * This inner class implements the behavior of the "receptionist", whose + * responsibility is to listen for incoming connection requests. As soon as a + * new client has arrived, the receptionist delegates the processing to a + * "servant" who will execute on its own thread. + */ + private class ReceptionistWorker implements Runnable { + + @Override + public void run() { + ServerSocket serverSocket; + + try { + serverSocket = new ServerSocket(port); + } catch (IOException ex) { + LOG.log(Level.SEVERE, null, ex); + return; + } + + while (true) { + LOG.log(Level.INFO, "Waiting (blocking) for a new client on port {0}", port); + try { + Socket clientSocket = serverSocket.accept(); + LOG.info("A new client has arrived. Starting a new thread and delegating work to a new servant..."); + new Thread(new ServantWorker(clientSocket)).start(); + } catch (IOException ex) { + Logger.getLogger(MultiThreadedServer.class.getName()).log(Level.SEVERE, null, ex); + } + } + + } + + /** + * This inner class implements the behavior of the "servants", whose + * responsibility is to take care of clients once they have connected. This + * is where we implement the application protocol logic, i.e. where we read + * data sent by the client and where we generate the responses. + */ + private class ServantWorker implements Runnable { + + Socket clientSocket; + BufferedReader in = null; + PrintWriter out = null; + + public ServantWorker(Socket clientSocket) { + try { + this.clientSocket = clientSocket; + in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); + out = new PrintWriter(clientSocket.getOutputStream()); + } catch (IOException ex) { + Logger.getLogger(MultiThreadedServer.class.getName()).log(Level.SEVERE, null, ex); + } + } + + @Override + public void run() { + String line; + boolean shouldRun = true; + + out.println("Welcome to the Multi-Threaded Server.\nSend me text lines and conclude with the QUIT command."); + out.flush(); + try { + LOG.info("Reading until client sends BYE or closes the connection..."); + while ((shouldRun) && (line = in.readLine()) != null) { + int nbArgs = line.split(" ").length; + if (line.equalsIgnoreCase("quit")) { + shouldRun = false; + } else if (nbArgs == 4 && line.split(" ")[0].equalsIgnoreCase("compute")){ + boolean numOk = true; + double num1 =0, num2=0; + try { + num1 = Double.parseDouble(line.split(" ")[2]); + num2 = Double.parseDouble(line.split(" ")[3]); + } catch (NumberFormatException nfe) { + out.println("Error 400. Syntax error\r\n"); + out.println("Must be number\n"); + numOk = false; + } + if (numOk) { + double result; + switch (line.split(" ")[1]) { + case "ADD": + case "add": + result = num1 + num2; + out.println(num1 + "+" + num2 + "=" + result +"\r\n"); + break; + case "MULT": + case "mult": + result = num1 * num2; + out.println(num1 + "*" + num2 + "=" + result +"\r\n"); + break; + } + } + + } + + out.println("> " + line.toUpperCase()); + out.flush(); + } + + LOG.info("Cleaning up resources..."); + clientSocket.close(); + in.close(); + out.close(); + + } catch (IOException ex) { + if (in != null) { + try { + in.close(); + } catch (IOException ex1) { + LOG.log(Level.SEVERE, ex1.getMessage(), ex1); + } + } + if (out != null) { + out.close(); + } + if (clientSocket != null) { + try { + clientSocket.close(); + } catch (IOException ex1) { + LOG.log(Level.SEVERE, ex1.getMessage(), ex1); + } + } + LOG.log(Level.SEVERE, ex.getMessage(), ex); + } + } + } + } +} diff --git a/my-app/src/main/java/com/mycompany/app/SingleThreadedServer.java b/my-app/src/main/java/com/mycompany/app/SingleThreadedServer.java new file mode 100644 index 0000000..1967673 --- /dev/null +++ b/my-app/src/main/java/com/mycompany/app/SingleThreadedServer.java @@ -0,0 +1,130 @@ +package com.mycompany.app; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.PrintWriter; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * This class implements a single-threaded TCP server. It is able to interact + * with only one client at the time. If a client tries to connect when + * the server is busy with another one, it will have to wait. + * + * @author Olivier Liechti + */ +public class SingleThreadedServer { + + final static Logger LOG = Logger.getLogger(SingleThreadedServer.class.getName()); + + int port; + + /** + * Constructor + * @param port the port to listen on + */ + public SingleThreadedServer(int port) { + this.port = port; + } + + /** + * This method initiates the process. The server creates a socket and binds + * it to the previously specified port. It then waits for clients in a infinite + * loop. When a client arrives, the server will read its input line by line + * and send back the data converted to uppercase. This will continue until + * the client sends the "BYE" command. + */ + public void serveClients() { + ServerSocket serverSocket; + Socket clientSocket = null; + BufferedReader in = null; + PrintWriter out = null; + + try { + serverSocket = new ServerSocket(port); + } catch (IOException ex) { + LOG.log(Level.SEVERE, null, ex); + return; + } + + while (true) { + try { + + LOG.log(Level.INFO, "Waiting (blocking) for a new client on port {0}", port); + clientSocket = serverSocket.accept(); + in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); + out = new PrintWriter(clientSocket.getOutputStream()); + String line; + boolean shouldRun = true; + + out.println("Welcome to the Single-Threaded Server.\nSend me text lines and conclude with the QUIT command."); + out.flush(); + LOG.info("Reading until client sends quit or closes the connection..."); + while ( (shouldRun) && (line = in.readLine()) != null ) { + int nbArgs = line.split(" ").length; + if (line.equalsIgnoreCase("quit")) { + shouldRun = false; + } else if (nbArgs == 4 && line.split(" ")[0].equalsIgnoreCase("compute")){ + boolean numOk = true; + double num1 =0, num2=0; + try { + num1 = Double.parseDouble(line.split(" ")[2]); + num2 = Double.parseDouble(line.split(" ")[3]); + } catch (NumberFormatException nfe) { + out.println("Error 400. Syntax error\r\n"); + out.println("Must be number\n"); + numOk = false; + } + if (numOk) { + double result; + switch (line.split(" ")[1]) { + case "ADD": + case "add": + result = num1 + num2; + out.println(num1 + "+" + num2 + "=" + result +"\r\n"); + break; + case "MULT": + case "mult": + result = num1 * num2; + out.println(num1 + "*" + num2 + "=" + result +"\r\n"); + break; + } + } + } else { + out.println("Error 400. Syntax error\r\n"); + } + out.println("> " + line.toUpperCase()); + out.flush(); + } + + LOG.info("Cleaning up resources..."); + clientSocket.close(); + in.close(); + out.close(); + + } catch (IOException ex) { + if (in != null) { + try { + in.close(); + } catch (IOException ex1) { + LOG.log(Level.SEVERE, ex1.getMessage(), ex1); + } + } + if (out != null) { + out.close(); + } + if (clientSocket != null) { + try { + clientSocket.close(); + } catch (IOException ex1) { + LOG.log(Level.SEVERE, ex1.getMessage(), ex1); + } + } + LOG.log(Level.SEVERE, ex.getMessage(), ex); + } + } + } +} diff --git a/my-app/src/main/java/com/mycompany/app/TcpServers.java b/my-app/src/main/java/com/mycompany/app/TcpServers.java new file mode 100644 index 0000000..2b6b663 --- /dev/null +++ b/my-app/src/main/java/com/mycompany/app/TcpServers.java @@ -0,0 +1,37 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package com.mycompany.app; + +/** + * This application shows the difference between a single threaded TCP server + * and a multi threaded TCP server. It shows that the first one is only able to + * process one client at the time, which is obviously not really an option for + * most applications. The second one uses n+1 threads, where n is the number of + * clients currently connected. The extra thread is used to wait for new clients + * to arrive, in a loop. + * + * The application starts the multi-threaded server on port 2323 and the + * single-threaded server on port 2424. Use several terminals and the telnet + * command to (try to) connect to the servers and compare the behavior. + * + * @author Olivier Liechti + */ +public class TcpServers { + + /** + * @param args the command line arguments + */ + public static void main(String[] args) { + System.setProperty("java.util.logging.SimpleFormatter.format", "%5$s %n"); + + MultiThreadedServer multi = new MultiThreadedServer(2323); + multi.serveClients(); + + //SingleThreadedServer single = new SingleThreadedServer(2424); + //single.serveClients(); + } + +} diff --git a/my-app/src/test/java/com/mycompany/app/AppTest.java b/my-app/src/test/java/com/mycompany/app/AppTest.java new file mode 100644 index 0000000..81ac345 --- /dev/null +++ b/my-app/src/test/java/com/mycompany/app/AppTest.java @@ -0,0 +1,20 @@ +package com.mycompany.app; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +/** + * Unit test for simple App. + */ +public class AppTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() + { + assertTrue( true ); + } +} diff --git a/specs/CapitainMorgan/PROTOCOL.md b/specs/CapitainMorgan/PROTOCOL.md new file mode 100644 index 0000000..cbfb179 --- /dev/null +++ b/specs/CapitainMorgan/PROTOCOL.md @@ -0,0 +1,27 @@ +> What transport protocol do we use? + +TCP + +> How does the client find the server (addresses and ports)? + +port 2021 client, port 2021 serveur + +> Who speaks first? + +Client + +> What is the sequence of messages exchanged by the client and the server? (flow) + +Client -> mult 5 2 -> serveur -> 10 -> Client + +> What happens when a message is received from the other party? (semantics) + +le verifie et le traite + +> What is the syntax of the messages? How we generate and parse them? (syntax) + +add 5 2,minus 5 2, mult 5 2, div 5 2, + +> Who closes the connection and when? + +serveur après 2min \ No newline at end of file diff --git a/specs/issolahma/protocol.md b/specs/issolahma/protocol.md new file mode 100644 index 0000000..0eb35c9 --- /dev/null +++ b/specs/issolahma/protocol.md @@ -0,0 +1,57 @@ + * What transport protocol do we use? + + TCP + + + + * How does the client find the server (addresses and ports)? + + IP:2828 + + + + * Who speaks first? + + client + + + + * What is the sequence of messages exchanged by the client and the server? (flow) + + client -> askconn -> server + + server -> connOk -> client + + client -> operation -> server + + server -> result/error -> client + + + + * What happens when a message is received from the other party? (semantics) + + - check the message + + - respond to the message + + + + * What is the syntax of the messages? How we generate and parse them? (syntax) + + add num num + + mult num num + + askConn + + connOk + + connErr + + syntaxError + + + + * Who closes the connection and when? + + server after 2min no msg \ No newline at end of file