diff --git a/examples/04-StreamingTimeServer/StreamingTimeServer/pom.xml b/examples/04-StreamingTimeServer/StreamingTimeServer/pom.xml
new file mode 100644
index 0000000..660960d
--- /dev/null
+++ b/examples/04-StreamingTimeServer/StreamingTimeServer/pom.xml
@@ -0,0 +1,40 @@
+
+
+ 4.0.0
+ ch.heigvd.res.examples
+ StreamingTimeServer
+ 1.0-SNAPSHOT
+ jar
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.3
+
+
+ package
+
+ shade
+
+
+ true
+ standalone
+
+
+ ch.heigvd.res.examples.StreamingTimeServer
+
+
+
+
+
+
+
+
+
+ UTF-8
+ 1.7
+ 1.7
+
+ StreamingTimeServer
+
\ No newline at end of file
diff --git a/examples/04-StreamingTimeServer/StreamingTimeServer/src/main/java/ch/heigvd/res/examples/StreamingTimeServer.java b/examples/04-StreamingTimeServer/StreamingTimeServer/src/main/java/ch/heigvd/res/examples/StreamingTimeServer.java
new file mode 100644
index 0000000..46e5c39
--- /dev/null
+++ b/examples/04-StreamingTimeServer/StreamingTimeServer/src/main/java/ch/heigvd/res/examples/StreamingTimeServer.java
@@ -0,0 +1,134 @@
+package ch.heigvd.res.examples;
+
+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.Date;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A very simple example of TCP server. When the server starts, it binds a
+ * server socket on any of the available network interfaces and on port 2205. It
+ * then waits until one (only one!) client makes a connection request. When the
+ * client arrives, the server does not even check if the client sends data. It
+ * simply writes the current time, every second, during 15 seconds.
+ *
+ * To test the server, simply open a terminal, do a "telnet localhost 2205" and
+ * see what you get back. Use Wireshark to have a look at the transmitted TCP
+ * segments.
+ *
+ * @author Olivier Liechti
+ */
+public class StreamingTimeServer {
+
+ static final Logger LOG = Logger.getLogger(StreamingTimeServer.class.getName());
+
+ private final int TEST_DURATION = 15000;
+ private final int PAUSE_DURATION = 1000;
+ private final int NUMBER_OF_ITERATIONS = TEST_DURATION / PAUSE_DURATION;
+ private final int LISTEN_PORT = 2205;
+
+ /**
+ * This method does the entire processing.
+ */
+ public void start() {
+ LOG.info("Starting server...");
+
+ ServerSocket serverSocket = null;
+ Socket clientSocket = null;
+ BufferedReader reader = null;
+ PrintWriter writer = null;
+
+ try {
+ LOG.log(Level.INFO, "Creating a server socket and binding it on any of the available network interfaces and on port {0}", new Object[]{Integer.toString(LISTEN_PORT)});
+ serverSocket = new ServerSocket(LISTEN_PORT);
+ logServerSocketAddress(serverSocket);
+
+ while (true) {
+ LOG.log(Level.INFO, "Waiting (blocking) for a connection request on {0} : {1}", new Object[]{serverSocket.getInetAddress(), Integer.toString(serverSocket.getLocalPort())});
+ clientSocket = serverSocket.accept();
+
+ LOG.log(Level.INFO, "A client has arrived. We now have a client socket with following attributes:");
+ logSocketAddress(clientSocket);
+
+ LOG.log(Level.INFO, "Getting a Reader and a Writer connected to the client socket...");
+ reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+ writer = new PrintWriter(clientSocket.getOutputStream());
+
+ LOG.log(Level.INFO, "Starting my job... sending current time to the client for {0} ms", TEST_DURATION);
+ for (int i = 0; i < NUMBER_OF_ITERATIONS; i++) {
+ writer.println(String.format("{'time' : '%s'}", new Date()));
+ writer.flush();
+ LOG.log(Level.INFO, "Sent data to client, doing a pause...");
+ Thread.sleep(PAUSE_DURATION);
+ }
+
+ reader.close();
+ writer.close();
+ clientSocket.close();
+
+
+ }
+
+ } catch (IOException | InterruptedException ex) {
+ LOG.log(Level.SEVERE, ex.getMessage());
+ } finally {
+ LOG.log(Level.INFO, "We are done. Cleaning up resources, closing streams and sockets...");
+ try {
+ reader.close();
+ } catch (IOException ex) {
+ Logger.getLogger(StreamingTimeServer.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ writer.close();
+ try {
+ clientSocket.close();
+ } catch (IOException ex) {
+ Logger.getLogger(StreamingTimeServer.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ try {
+ serverSocket.close();
+ } catch (IOException ex) {
+ Logger.getLogger(StreamingTimeServer.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ }
+
+ }
+
+ /**
+ * A utility method to print server socket information
+ *
+ * @param serverSocket the socket that we want to log
+ */
+ private void logServerSocketAddress(ServerSocket serverSocket) {
+ LOG.log(Level.INFO, " Local IP address: {0}", new Object[]{serverSocket.getLocalSocketAddress()});
+ LOG.log(Level.INFO, " Local port: {0}", new Object[]{Integer.toString(serverSocket.getLocalPort())});
+ LOG.log(Level.INFO, " is bound: {0}", new Object[]{serverSocket.isBound()});
+ }
+
+ /**
+ * A utility method to print socket information
+ *
+ * @param clientSocket the socket that we want to log
+ */
+ private void logSocketAddress(Socket clientSocket) {
+ LOG.log(Level.INFO, " Local IP address: {0}", new Object[]{clientSocket.getLocalAddress()});
+ LOG.log(Level.INFO, " Local port: {0}", new Object[]{Integer.toString(clientSocket.getLocalPort())});
+ LOG.log(Level.INFO, " Remote Socket address: {0}", new Object[]{clientSocket.getRemoteSocketAddress()});
+ LOG.log(Level.INFO, " Remote port: {0}", new Object[]{Integer.toString(clientSocket.getPort())});
+ }
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) {
+ System.setProperty("java.util.logging.SimpleFormatter.format", "%5$s %n");
+
+ StreamingTimeServer server = new StreamingTimeServer();
+ server.start();
+ }
+
+}
diff --git a/examples/05-DumbHttpClient/DumbHttpClient/pom.xml b/examples/05-DumbHttpClient/DumbHttpClient/pom.xml
new file mode 100644
index 0000000..c35b865
--- /dev/null
+++ b/examples/05-DumbHttpClient/DumbHttpClient/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/examples/05-DumbHttpClient/DumbHttpClient/src/main/java/ch/heigvd/res/examples/DumbHttpClient.java b/examples/05-DumbHttpClient/DumbHttpClient/src/main/java/ch/heigvd/res/examples/DumbHttpClient.java
new file mode 100644
index 0000000..0df8749
--- /dev/null
+++ b/examples/05-DumbHttpClient/DumbHttpClient/src/main/java/ch/heigvd/res/examples/DumbHttpClient.java
@@ -0,0 +1,80 @@
+package ch.heigvd.res.examples;
+
+import java.io.*;
+import java.net.Socket;
+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 sendWrongHttpRequest() {
+ Socket clientSocket = null;
+ OutputStream os = null;
+ InputStream is = null;
+
+ try {
+ clientSocket = new Socket("www.lematin.ch", 80);
+ os = clientSocket.getOutputStream();
+ is = clientSocket.getInputStream();
+
+ String malformedHttpRequest = "Hello, sorry, but I don't speak HTTP...\r\n\r\n";
+ os.write(malformedHttpRequest.getBytes());
+
+ 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());
+ } 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);
+ }
+ }
+ }
+
+ /**
+ * @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.sendWrongHttpRequest();
+
+ }
+
+}
diff --git a/examples/06-PresenceApplication/PresenceApplication/pom.xml b/examples/06-PresenceApplication/PresenceApplication/pom.xml
new file mode 100644
index 0000000..1b87ea4
--- /dev/null
+++ b/examples/06-PresenceApplication/PresenceApplication/pom.xml
@@ -0,0 +1,40 @@
+
+
+ 4.0.0
+ ch.heigvd.res.examples
+ PresenceApplication
+ 1.0-SNAPSHOT
+ jar
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 2.3
+
+
+ package
+
+ shade
+
+
+ true
+ standalone
+
+
+ ch.heigvd.res.examples.PresenceApplication
+
+
+
+
+
+
+
+
+
+ UTF-8
+ 1.7
+ 1.7
+
+ PresenceApplication
+
\ No newline at end of file
diff --git a/examples/06-PresenceApplication/PresenceApplication/src/main/java/ch/heigvd/res/examples/PresenceApplication.java b/examples/06-PresenceApplication/PresenceApplication/src/main/java/ch/heigvd/res/examples/PresenceApplication.java
new file mode 100644
index 0000000..ff36e57
--- /dev/null
+++ b/examples/06-PresenceApplication/PresenceApplication/src/main/java/ch/heigvd/res/examples/PresenceApplication.java
@@ -0,0 +1,38 @@
+package ch.heigvd.res.examples;
+
+/**
+ * The server reacts to the following commands, defined in the protocol:
+ * - HELLO name: the user "behind" the client is not anonymous anymore
+ * - SAY message: the message is broadcasted to connected clients
+ * - WHO: the server returns the list of connected users
+ * - BYE: the client is disconnected and the others are notified
+ *
+ * @author Olivier Liechti
+ */
+public class PresenceApplication {
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) {
+ System.setProperty("java.util.logging.SimpleFormatter.format", "%5$s %n");
+
+ Thread listenThread = new Thread(new PresenceServer());
+ listenThread.start();
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+
+ PresenceClient c1 = new PresenceClient();
+ c1.connect("localhost", Protocol.PRESENCE_DEFAULT_PORT, "Sacha");
+ new PresenceClient().connect("localhost", Protocol.PRESENCE_DEFAULT_PORT, "Fabienne");
+ new PresenceClient().connect("localhost", Protocol.PRESENCE_DEFAULT_PORT, "Olivier");
+ c1.disconnect();
+ new PresenceClient().connect("localhost", Protocol.PRESENCE_DEFAULT_PORT, "Jean");
+ new PresenceClient().connect("localhost", Protocol.PRESENCE_DEFAULT_PORT, "Nicole");
+
+ }
+
+}
diff --git a/examples/06-PresenceApplication/PresenceApplication/src/main/java/ch/heigvd/res/examples/PresenceClient.java b/examples/06-PresenceApplication/PresenceApplication/src/main/java/ch/heigvd/res/examples/PresenceClient.java
new file mode 100644
index 0000000..4670146
--- /dev/null
+++ b/examples/06-PresenceApplication/PresenceApplication/src/main/java/ch/heigvd/res/examples/PresenceClient.java
@@ -0,0 +1,112 @@
+package ch.heigvd.res.examples;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.net.Socket;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class implements a simple client for our custom presence protocol.
+ * When the client connects to a server, a thread is started to listen for
+ * notifications sent by the server.
+ *
+ * @author Olivier Liechti
+ */
+public class PresenceClient {
+
+ final static Logger LOG = Logger.getLogger(PresenceClient.class.getName());
+
+ Socket clientSocket;
+ BufferedReader in;
+ PrintWriter out;
+ boolean connected = false;
+ String userName;
+
+ /**
+ * This inner class implements the Runnable interface, so that the run()
+ * method can execute on its own thread. This method reads data sent from the
+ * server, line by line, until the connection is closed or lost.
+ */
+ class NotificationListener implements Runnable {
+
+ @Override
+ public void run() {
+ String notification;
+ try {
+ while ((connected && (notification = in.readLine()) != null)) {
+ LOG.log(Level.INFO, "Server notification for {1}: {0}", new Object[]{notification,userName});
+ }
+ } catch (IOException e) {
+ LOG.log(Level.SEVERE, "Connection problem in client used by {1}: {0}", new Object[]{e.getMessage(),userName});
+ connected = false;
+ } finally {
+ cleanup();
+ }
+ }
+ }
+
+ /**
+ * This method is used to connect to the server and to inform the server that
+ * the user "behind" the client has a name (in other words, the HELLO command
+ * is issued after successful connection).
+ *
+ * @param serverAddress the IP address used by the Presence Server
+ * @param serverPort the port used by the Presence Server
+ * @param userName the name of the user, used as a parameter for the HELLO command
+ */
+ public void connect(String serverAddress, int serverPort, String userName) {
+ try {
+ clientSocket = new Socket(serverAddress, serverPort);
+ in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+ out = new PrintWriter(clientSocket.getOutputStream());
+ connected = true;
+ this.userName = userName;
+ } catch (IOException e) {
+ LOG.log(Level.SEVERE, "Unable to connect to server: {0}", e.getMessage());
+ cleanup();
+ return;
+ }
+ // Let us start a thread, so that we can listen for server notifications
+ new Thread(new NotificationListener()).start();
+
+ // Let us send the HELLO command to inform the server about who the user
+ // is. Other clients will be notified.
+ out.println("HELLO " + userName);
+ out.flush();
+ }
+
+ public void disconnect() {
+ LOG.log(Level.INFO, "{0} has requested to be disconnected.", userName);
+ connected = false;
+ out.println("BYE");
+ cleanup();
+ }
+
+ private void cleanup() {
+
+ try {
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException ex) {
+ LOG.log(Level.SEVERE, ex.getMessage(), ex);
+ }
+
+ if (out != null) {
+ out.close();
+ }
+
+ try {
+ if (clientSocket != null) {
+ clientSocket.close();
+ }
+ } catch (IOException ex) {
+ LOG.log(Level.SEVERE, ex.getMessage(), ex);
+ }
+ }
+
+
+}
diff --git a/examples/06-PresenceApplication/PresenceApplication/src/main/java/ch/heigvd/res/examples/PresenceServer.java b/examples/06-PresenceApplication/PresenceApplication/src/main/java/ch/heigvd/res/examples/PresenceServer.java
new file mode 100644
index 0000000..517a248
--- /dev/null
+++ b/examples/06-PresenceApplication/PresenceApplication/src/main/java/ch/heigvd/res/examples/PresenceServer.java
@@ -0,0 +1,213 @@
+package ch.heigvd.res.examples;
+
+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.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * This class is a multi-threaded server of the custom presence protocol. The
+ * server binds a socket on the specified port and waits for incoming connection
+ * requests. It keeps track of connected clients in a list. When new clients
+ * arrive, leave or send messages, the server notifies all connected clients.
+ *
+ * @author Olivier Liechti
+ */
+public class PresenceServer implements Runnable {
+
+ final static Logger LOG = Logger.getLogger(PresenceServer.class.getName());
+
+ boolean shouldRun;
+ ServerSocket serverSocket;
+ final List connectedWorkers;
+
+ public PresenceServer() {
+ this.shouldRun = true;
+ this.connectedWorkers = Collections.synchronizedList(new LinkedList());
+ }
+
+ private void registerWorker(Worker worker) {
+ LOG.log(Level.INFO, ">> Waiting for lock before registring worker {0}", worker.userName);
+ connectedWorkers.add(worker);
+ LOG.log(Level.INFO, "<< Worker {0} registered.", worker.userName);
+ }
+
+ private void unregisterWorker(Worker worker) {
+ LOG.log(Level.INFO, ">> Waiting for lock before unregistring worker {0}", worker.userName);
+ connectedWorkers.remove(worker);
+ LOG.log(Level.INFO, "<< Worker {0} unregistered.", worker.userName);
+ }
+
+ private void notifyConnectedWorkers(String message) {
+ LOG.info(">> Waiting for lock before notifying workers");
+ synchronized (connectedWorkers) {
+ LOG.info("Notifying workers");
+ for (Worker worker : connectedWorkers) {
+ worker.sendNotification(message);
+ }
+ }
+ LOG.info("<< Workers notified");
+ }
+
+ private void disconnectConnectedWorkers() {
+ LOG.info(">> Waiting for lock before disconnecting workers");
+ synchronized (connectedWorkers) {
+ LOG.info("Disconnecting workers");
+ for (Worker worker : connectedWorkers) {
+ worker.disconnect();
+ }
+ }
+ LOG.info("<< Workers disconnected");
+ }
+
+ @Override
+ public void run() {
+ try {
+ LOG.log(Level.INFO, "Starting Presence Server on port {0}", Protocol.PRESENCE_DEFAULT_PORT);
+ serverSocket = new ServerSocket(Protocol.PRESENCE_DEFAULT_PORT);
+ while (shouldRun) {
+ Socket clientSocket = serverSocket.accept();
+ PresenceServer.this.notifyConnectedWorkers("Someone has arrived...");
+ Worker newWorker = new Worker(clientSocket);
+ registerWorker(newWorker);
+ new Thread(newWorker).start();
+ }
+ serverSocket.close();
+ LOG.info("shouldRun is false... server going down");
+ } catch (IOException ex) {
+ LOG.log(Level.SEVERE, ex.getMessage(), ex);
+ System.exit(-1);
+ }
+ }
+
+ private void shutdown() {
+ LOG.info("Shutting down server...");
+ shouldRun = false;
+ try {
+ serverSocket.close();
+ } catch (IOException ex) {
+ LOG.log(Level.SEVERE, ex.getMessage(), ex);
+ }
+ disconnectConnectedWorkers();
+ }
+
+ class Worker implements Runnable {
+
+ Socket clientSocket;
+ BufferedReader in;
+ PrintWriter out;
+ boolean connected;
+ String userName = "An anonymous user";
+
+ public Worker(Socket clientSocket) {
+ this.clientSocket = clientSocket;
+ try {
+ in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
+ out = new PrintWriter(clientSocket.getOutputStream());
+ connected = true;
+ } catch (IOException ex) {
+ LOG.log(Level.SEVERE, ex.getMessage(), ex);
+ }
+ }
+
+ @Override
+ public void run() {
+ String commandLine;
+ PresenceServer.this.notifyConnectedWorkers("Welcome to the Presence Server");
+ PresenceServer.this.notifyConnectedWorkers(" Tell me who you are with 'HELLO name'");
+ PresenceServer.this.notifyConnectedWorkers(" Say something to other users with 'SAY message'");
+ PresenceServer.this.notifyConnectedWorkers(" Ask me who is connected with 'WHO'");
+ PresenceServer.this.notifyConnectedWorkers(" Leave with 'BYE'");
+ PresenceServer.this.notifyConnectedWorkers(" Shutdown server with 'KILL'");
+ try {
+ while (connected && ((commandLine = in.readLine()) != null)) {
+ String[] tokens = commandLine.split(" ");
+ switch (tokens[0].toUpperCase()) {
+ case (Protocol.CMD_HELLO):
+ userName = tokens.length >= 2 ? tokens[1] : "An anonymous user";
+ PresenceServer.this.notifyConnectedWorkers(userName + " is in the room.");
+ break;
+ case (Protocol.CMD_SAY):
+ String message = tokens.length >= 2 ? commandLine.substring(4) : "nothing...";
+ PresenceServer.this.notifyConnectedWorkers(userName + " says: " + message);
+ break;
+ case (Protocol.CMD_WHO):
+ StringBuilder sb = new StringBuilder("Currently connected users:\r\n");
+ for (Worker w : connectedWorkers) {
+ sb.append(" - ");
+ sb.append(w.userName);
+ sb.append("\n");
+ }
+ sendNotification(sb.toString());
+ break;
+ case (Protocol.CMD_BYE):
+ PresenceServer.this.notifyConnectedWorkers(userName + " is about to leave the room.");
+ connected = false;
+ break;
+ case (Protocol.CMD_KILL):
+ sendNotification("KILL command received. Bringing server down...");
+ shutdown();
+ break;
+ default:
+ sendNotification("What? I only understand HELLO, SAY, WHO, BYE and KILL commands");
+ }
+ }
+ } catch (IOException ex) {
+ LOG.log(Level.SEVERE, ex.getMessage(), ex);
+ } finally {
+ unregisterWorker(this);
+ PresenceServer.this.notifyConnectedWorkers(userName + " has left the room.");
+ cleanup();
+ }
+ }
+
+ private void cleanup() {
+ LOG.log(Level.INFO, "Cleaning up worker used by {0}", userName);
+
+ LOG.log(Level.INFO, "Closing clientSocket used by {0}", userName);
+ try {
+ if (clientSocket != null) {
+ clientSocket.close();
+ }
+ } catch (IOException ex) {
+ LOG.log(Level.SEVERE, ex.getMessage(), ex);
+ }
+
+ LOG.log(Level.INFO, "Closing in used by {0}", userName);
+ try {
+ if (in != null) {
+ in.close();
+ }
+ } catch (IOException ex) {
+ LOG.log(Level.SEVERE, ex.getMessage(), ex);
+ }
+
+ LOG.log(Level.INFO, "Closing out used by {0}", userName);
+ if (out != null) {
+ out.close();
+ }
+
+ LOG.log(Level.INFO, "Clean up done for worker used by {0}", userName);
+ }
+
+ public void sendNotification(String message) {
+ out.println(message);
+ out.flush();
+ }
+
+ private void disconnect() {
+ LOG.log(Level.INFO, "Disconnecting worker used by {0}", userName);
+ connected = false;
+ cleanup();
+ }
+
+ }
+
+}
diff --git a/examples/06-PresenceApplication/PresenceApplication/src/main/java/ch/heigvd/res/examples/Protocol.java b/examples/06-PresenceApplication/PresenceApplication/src/main/java/ch/heigvd/res/examples/Protocol.java
new file mode 100644
index 0000000..3a807b5
--- /dev/null
+++ b/examples/06-PresenceApplication/PresenceApplication/src/main/java/ch/heigvd/res/examples/Protocol.java
@@ -0,0 +1,17 @@
+package ch.heigvd.res.examples;
+
+/**
+ *
+ * @author Olivier Liechti
+ */
+public class Protocol {
+
+ public static final int PRESENCE_DEFAULT_PORT = 9907;
+
+ public static final String CMD_HELLO = "HELLO";
+ public static final String CMD_WHO = "WHO";
+ public static final String CMD_BYE = "BYE";
+ public static final String CMD_SAY = "SAY";
+ public static final String CMD_KILL = "KILL";
+
+}
diff --git a/examples/07-TcpServers/TcpServers/pom.xml b/examples/07-TcpServers/TcpServers/pom.xml
new file mode 100644
index 0000000..7399bec
--- /dev/null
+++ b/examples/07-TcpServers/TcpServers/pom.xml
@@ -0,0 +1,39 @@
+
+
+ 4.0.0
+ ch.heigvd.res.examples
+ TcpServers
+ 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.8
+ 1.8
+
+
\ No newline at end of file
diff --git a/examples/07-TcpServers/TcpServers/src/main/java/ch/heigvd/res/examples/MultiThreadedServer.java b/examples/07-TcpServers/TcpServers/src/main/java/ch/heigvd/res/examples/MultiThreadedServer.java
new file mode 100644
index 0000000..8ce2bc9
--- /dev/null
+++ b/examples/07-TcpServers/TcpServers/src/main/java/ch/heigvd/res/examples/MultiThreadedServer.java
@@ -0,0 +1,145 @@
+package ch.heigvd.res.examples;
+
+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 BYE command.");
+ out.flush();
+ try {
+ LOG.info("Reading until client sends BYE or closes the connection...");
+ while ((shouldRun) && (line = in.readLine()) != null) {
+ if (line.equalsIgnoreCase("bye")) {
+ shouldRun = false;
+ }
+ 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/examples/07-TcpServers/TcpServers/src/main/java/ch/heigvd/res/examples/SingleThreadedServer.java b/examples/07-TcpServers/TcpServers/src/main/java/ch/heigvd/res/examples/SingleThreadedServer.java
new file mode 100644
index 0000000..ef48f90
--- /dev/null
+++ b/examples/07-TcpServers/TcpServers/src/main/java/ch/heigvd/res/examples/SingleThreadedServer.java
@@ -0,0 +1,101 @@
+package ch.heigvd.res.examples;
+
+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 BYE command.");
+ out.flush();
+ LOG.info("Reading until client sends BYE or closes the connection...");
+ while ( (shouldRun) && (line = in.readLine()) != null ) {
+ if (line.equalsIgnoreCase("bye")) {
+ shouldRun = false;
+ }
+ 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/examples/07-TcpServers/TcpServers/src/main/java/ch/heigvd/res/examples/TcpServers.java b/examples/07-TcpServers/TcpServers/src/main/java/ch/heigvd/res/examples/TcpServers.java
new file mode 100644
index 0000000..0327cd1
--- /dev/null
+++ b/examples/07-TcpServers/TcpServers/src/main/java/ch/heigvd/res/examples/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 ch.heigvd.res.examples;
+
+/**
+ * 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();
+ }
+
+}