Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client and Server #41

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# Teaching-HEIGVD-RES-2020-Exercise-Protocol-Design
The first goal is to **specify a client-server protocol**, which will allow a client to ask a server compute a calculation and to return the result.
The first goal is to **specify a KurohanJuri.client-KurohanJuri.server protocol**, which will allow a KurohanJuri.client to ask a KurohanJuri.server compute a calculation and to return the result.

![](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d1/Calculator_on_macOS.png/381px-Calculator_on_macOS.png)



The specification must contain <u>**everything**</u> that is needed for **one person to implement a client**, for **another person to implement a server** and for the 2 applications to work together. The specification is a **contract** between the server and the client.
The specification must contain <u>**everything**</u> that is needed for **one person to implement a KurohanJuri.client**, for **another person to implement a KurohanJuri.server** and for the 2 applications to work together. The specification is a **contract** between the KurohanJuri.server and the KurohanJuri.client.

## Phase 1: write the specification (15 minutes)

* Fork this repo and **create a folder with your GitHub ID in the "specs" folder** (like you did for the Chill lab at the beginning of the semester)
* In this folder, create a file named `PROTOCOL.md` and write your specification:
* What transport protocol do we use?
* How does the client find the server (addresses and ports)?
* How does the KurohanJuri.client find the KurohanJuri.server (addresses and ports)?
* Who speaks first?
* What is the sequence of messages exchanged by the client and the server? (flow)
* What is the sequence of messages exchanged by the KurohanJuri.client and the KurohanJuri.server? (flow)
* What happens when a message is received from the other party? (semantics)
* What is the syntax of the messages? How we generate and parse them? (syntax)
* Who closes the connection and when?
Expand All @@ -29,17 +29,17 @@ The specification must contain <u>**everything**</u> that is needed for **one pe
## Phase 3: validate specs in pairs (15 minutes)

* Work with the student sitting next to you
* One of them is the server, the other is the client
* One of them is the KurohanJuri.server, the other is the KurohanJuri.client
* Pick one of the 2 specifications
* One student uses netcat (nc) to start a server (nc -kl). The other student uses netcat to start a client.
* One student uses netcat (nc) to start a KurohanJuri.server (nc -kl). The other student uses netcat to start a KurohanJuri.client.
* Go through a couple of scenarios to validate that your specification is complete (**if you need to ask something to the other student, or if you need to discuss, you probably should make your specification more complete**)

## Phase 4: implement a client and a server (45 minutes)
## Phase 4: implement a KurohanJuri.client and a KurohanJuri.server (45 minutes)

- One student implements a client in Java
- The other student implements a server in Java
- The team performs various tests to validate that the client and the server work together (on the same machine, across two machines connected to the same network, in Docker containers)
- Add your code in your folder and submit a PR on the upstream server.
- One student implements a KurohanJuri.client in Java
- The other student implements a KurohanJuri.server in Java
- The team performs various tests to validate that the KurohanJuri.client and the KurohanJuri.server work together (on the same machine, across two machines connected to the same network, in Docker containers)
- Add your code in your folder and submit a PR on the upstream KurohanJuri.server.



17 changes: 17 additions & 0 deletions Teaching-HEIGVD-RES-2020-Exercise-Protocol-Design.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/client/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/server/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
Binary file not shown.
34 changes: 34 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>KurohanaJuir</groupId>
<artifactId>Teaching-HEIGVD-RES-2020-Exercise-Protocol-Design</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<build>
<plugins>
<plugin>
<!-- Build an executable JAR -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>KurohanaJuri.Main</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>

<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>

Binary file added specs/KurohanaJuri/Images/TCP.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 13 additions & 0 deletions specs/KurohanaJuri/PROTOCOL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Protocol

* Transport protocol : TCP
* Adredress and port : `10.192.91.10:220`
* Speaks first : KurohanJuri.client.Client
* Flow :
![](Images/TCP.gif)
* Semantics :
* The message is decoded with utf8
* Send a proper response back
* Syntax :
`Message {CONNECTION|COMPUTE|END} [Operation] [val1] [val2]`
* Closed by : KurohanJuri.client.Client
20 changes: 20 additions & 0 deletions src/main/java/KurohanaJuri/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package KurohanaJuri;

import KurohanaJuri.client.Client;
import KurohanaJuri.server.CalculatorServer;

public class Main {
private final static String HOSTNAME = "10.192.91.10";
private final static String LOCALHOST = "127.0.0.1";
private final static int PORT = 9999;

public static void main(String[] args) {
if(args[0].equals("Client")){
new Client(LOCALHOST, PORT);
} else if(args[0].equals("Server")){
new CalculatorServer(PORT);
} else {
System.out.println("Command unknown");
}
}
}
59 changes: 59 additions & 0 deletions src/main/java/KurohanaJuri/client/Client.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package KurohanaJuri.client;

import java.io.*;
import java.net.Socket;

public class Client {


public Client(String hostname, int port) {
// Create a client and try to connect
try (Socket clientSocket = new Socket(hostname, port)) {

InputStream is = clientSocket.getInputStream();
OutputStream os = clientSocket.getOutputStream();

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

// Read welcome messages
BufferedReader reader = new BufferedReader(new InputStreamReader(is));

// Show the server response
String line = reader.readLine();
System.out.println(line);

line = reader.readLine();
System.out.println(line);

String userInput;

userInput = br.readLine();

while (!userInput.equals("END")) {
// Read the user input

String[] inputArray = userInput.split(" ");


// Write through the socket
PrintWriter writer = new PrintWriter(os);

writer.println("COMPUTE " + inputArray[1] + " " + inputArray[0] + " " + inputArray[2]);
writer.flush();

// Wait the server response
System.out.println("Waiting server response...");

// Show the server response
line = reader.readLine();
System.out.println(line);

userInput = br.readLine();
}

} catch (IOException e) {
e.printStackTrace();
}

}
}
150 changes: 150 additions & 0 deletions src/main/java/KurohanaJuri/server/CalculatorMultiThread.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package KurohanaJuri.server;

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.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;

public class CalculatorMultiThread {

final static Logger LOG = Logger.getLogger(CalculatorMultiThread.class.getName());

int port;

/**
* Constructor
*
* @param port the port to listen on
*/
public CalculatorMultiThread(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(CalculatorMultiThread.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(CalculatorMultiThread.class.getName()).log(Level.SEVERE, null, ex);
}
}

@Override
public void run() {
String line;
boolean shouldRun = true;

out.println("Welcome to the Multi-Threaded Calculator Server.\nWhat operation would you like to do ? (quit with END line)");
out.flush();
try {
while ((shouldRun) && (line = in.readLine()) != null) {
if (line.equalsIgnoreCase("end")) {
shouldRun = false;
}

String[] inputArray = line.split(" ");

if (inputArray[0].equals("COMPUTE")) {
String[] subInputArray = Arrays.copyOfRange(inputArray, 1,4 );
if (CalculatorUtils.isInputValid(subInputArray)) {
double result = CalculatorUtils.computeInput(subInputArray);
out.println("= " + result);
out.flush();
} else {
out.println("Invalid input.");
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);
}
}
}
}
}
14 changes: 14 additions & 0 deletions src/main/java/KurohanaJuri/server/CalculatorServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package KurohanaJuri.server;

public class CalculatorServer {

public CalculatorServer(int port) {

//CalculatorSingleThread server = new CalculatorSingleThread(9999);
//server.serveClients();

CalculatorMultiThread serverMultithread = new CalculatorMultiThread(port);
serverMultithread.serveClients();
}

}
Loading