added Handout

This commit is contained in:
romanschenk37
2022-04-02 14:15:01 +02:00
parent 94c220fbb9
commit 0886f896b2
19 changed files with 1790 additions and 0 deletions
+58
View File
@@ -0,0 +1,58 @@
/*
* Gradle build configuration for specific lab module / exercise
*/
// enabled plugins
plugins {
// Support for Java
id 'java'
// Support for Java applications
id 'application'
}
// Project/Module information
description = 'Uebung Multichat Server'
group = 'ch.zhaw.pm2'
version = '2022.1'
// Dependency configuration
repositories {
// Use maven central for resolving dependencies.
mavenCentral()
}
dependencies {
// dependency to the protocol library
implementation project(':protocol')
// JUnit Jupiter dependencies
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.+'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.+'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.+'
}
// Configuration for Application plugin
application {
// Define the main class for the application.
mainClass = 'ch.zhaw.pm2.multichat.server.Server'
}
// Test task configuration
test {
// Use JUnit platform for unit tests
useJUnitPlatform()
}
// Java plugin configuration
java {
// By default the Java version of the gradle process is used as source/target version.
// This can be overridden, to ensure a specific version. Enable only if required.
sourceCompatibility = JavaVersion.VERSION_17 // ensure Java source code compatibility
// targetCompatibility = JavaVersion.VERSION_17 // version of the created byte-code
// Java compiler specific options
compileJava {
// source files should be UTF-8 encoded
options.encoding = 'UTF-8'
// for more options see https://docs.gradle.org/current/dsl/org.gradle.api.tasks.compile.CompileOptions.html
}
}
@@ -0,0 +1,96 @@
package ch.zhaw.pm2.multichat.server;
import ch.zhaw.pm2.multichat.protocol.NetworkHandler;
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
public class Server {
// Server connection
private NetworkHandler.NetworkServer<String> networkServer;
// Connection registry
private Map<String,ServerConnectionHandler> connections = new HashMap<>();
public static void main(String[] args) {
// Parse arguments for server port.
try {
int port;
switch (args.length) {
case 0 -> port = NetworkHandler.DEFAULT_PORT;
case 1 -> port = Integer.parseInt(args[0]);
default -> {
System.out.println("Illegal number of arguments: [<ServerPort>]");
return;
}
}
// Initialize server
final Server server = new Server(port);
// This adds a shutdown hook running a cleanup task if the JVM is terminated (kill -HUP, Ctrl-C,...)
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
try {
Thread.sleep(200);
System.out.println("Shutdown initiated...");
server.terminate();
} catch (InterruptedException e) {
System.out.println("Warning: Shutdown interrupted. " + e);
} finally {
System.out.println("Shutdown complete.");
}
}
});
// Start server
server.start();
} catch (IOException e) {
System.err.println("Error while starting server." + e.getMessage());
}
}
public Server(int serverPort) throws IOException {
// Open server connection
System.out.println("Create server connection");
networkServer = NetworkHandler.createServer(serverPort);
System.out.println("Listening on " + networkServer.getHostAddress() + ":" + networkServer.getHostPort());
}
private void start() {
System.out.println("Server started.");
try {
while (true) {
NetworkHandler.NetworkConnection<String> connection = networkServer.waitForConnection();
ServerConnectionHandler connectionHandler = new ServerConnectionHandler(connection, connections);
connectionHandler.startReceiving();
System.out.println(String.format("Connected new Client %s with IP:Port <%s:%d>",
connectionHandler.getUserName(),
connection.getRemoteHost(),
connection.getRemotePort()
));
}
} catch(SocketException e) {
System.out.println("Server connection terminated");
}
catch (IOException e) {
System.err.println("Communication error " + e);
}
// close server
System.out.println("Server Stopped.");
}
public void terminate() {
try {
System.out.println("Close server port.");
networkServer.close();
} catch (IOException e) {
System.err.println("Failed to close server connection: " + e);
}
}
}
@@ -0,0 +1,183 @@
package ch.zhaw.pm2.multichat.server;
import ch.zhaw.pm2.multichat.protocol.ChatProtocolException;
import ch.zhaw.pm2.multichat.protocol.NetworkHandler;
import java.io.EOFException;
import java.io.IOException;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Scanner;
import java.util.concurrent.atomic.AtomicInteger;
import static ch.zhaw.pm2.multichat.server.ServerConnectionHandler.State.*;
public class ServerConnectionHandler {
private static final AtomicInteger connectionCounter = new AtomicInteger(0);
private final int connectionId = connectionCounter.incrementAndGet();
private final NetworkHandler.NetworkConnection<String> connection;
private final Map<String,ServerConnectionHandler> connectionRegistry;
// Data types used for the Chat Protocol
private static final String DATA_TYPE_CONNECT = "CONNECT";
private static final String DATA_TYPE_CONFIRM = "CONFIRM";
private static final String DATA_TYPE_DISCONNECT = "DISCONNECT";
private static final String DATA_TYPE_MESSAGE = "MESSAGE";
private static final String DATA_TYPE_ERROR = "ERROR";
private static final String USER_NONE = "";
private static final String USER_ALL = "*";
private String userName = "Anonymous-"+connectionId;
private State state = NEW;
enum State {
NEW, CONNECTED, DISCONNECTED;
}
public ServerConnectionHandler(NetworkHandler.NetworkConnection<String> connection,
Map<String,ServerConnectionHandler> registry) {
Objects.requireNonNull(connection, "Connection must not be null");
Objects.requireNonNull(registry, "Registry must not be null");
this.connection = connection;
this.connectionRegistry = registry;
}
public String getUserName() {
return this.userName;
}
public void startReceiving() {
System.out.println("Starting Connection Handler for " + userName);
try {
System.out.println("Start receiving data...");
while (connection.isAvailable()) {
String data = connection.receive();
processData(data);
}
System.out.println("Stopped recieving data");
} catch (SocketException e) {
System.out.println("Connection terminated locally");
connectionRegistry.remove(userName);
System.out.println("Unregistered because client connection terminated: " + userName + " " + e.getMessage());
} catch (EOFException e) {
System.out.println("Connection terminated by remote");
connectionRegistry.remove(userName);
System.out.println("Unregistered because client connection terminated: " + userName + " " + e.getMessage());
} catch(IOException e) {
System.err.println("Communication error: " + e);
} catch(ClassNotFoundException e) {
System.err.println("Received object of unknown type: " + e.getMessage());
}
System.out.println("Stopping Connection Handler for " + userName);
}
public void stopReceiving() {
System.out.println("Closing Connection Handler for " + userName);
try {
System.out.println("Stop receiving data...");
connection.close();
System.out.println("Stopped receiving data.");
} catch (IOException e) {
System.err.println("Failed to close connection." + e);
}
System.out.println("Closed Connection Handler for " + userName);
}
private void processData(String data) {
try {
// parse data content
Scanner scanner = new Scanner(data);
String sender = null;
String reciever = null;
String type = null;
String payload = null;
if (scanner.hasNextLine()) {
sender = scanner.nextLine();
} else {
throw new ChatProtocolException("No Sender found");
}
if (scanner.hasNextLine()) {
reciever = scanner.nextLine();
} else {
throw new ChatProtocolException("No Reciever found");
}
if (scanner.hasNextLine()) {
type = scanner.nextLine();
} else {
throw new ChatProtocolException("No Type found");
}
if (scanner.hasNextLine()) {
payload = scanner.nextLine();
}
// dispatch operation based on type parameter
if (type.equals(DATA_TYPE_CONNECT)) {
if (this.state != NEW) throw new ChatProtocolException("Illegal state for connect request: " + state);
if (sender == null || sender.isBlank()) sender = this.userName;
if (connectionRegistry.containsKey(sender))
throw new ChatProtocolException("User name already taken: " + sender);
this.userName = sender;
connectionRegistry.put(userName, this);
sendData(USER_NONE, userName, DATA_TYPE_CONFIRM, "Registration successfull for " + userName);
this.state = CONNECTED;
} else if (type.equals(DATA_TYPE_CONFIRM)) {
System.out.println("Not expecting to receive a CONFIRM request from client");
} else if (type.equals(DATA_TYPE_DISCONNECT)) {
if (state == DISCONNECTED)
throw new ChatProtocolException("Illegal state for disconnect request: " + state);
if (state == CONNECTED) {
connectionRegistry.remove(this.userName);
}
sendData(USER_NONE, userName, DATA_TYPE_CONFIRM, "Confirm disconnect of " + userName);
this.state = DISCONNECTED;
this.stopReceiving();
} else if (type.equals(DATA_TYPE_MESSAGE)) {
if (state != CONNECTED) throw new ChatProtocolException("Illegal state for message request: " + state);
if (USER_ALL.equals(reciever)) {
for (ServerConnectionHandler handler : connectionRegistry.values()) {
handler.sendData(sender, reciever, type, payload);
}
} else {
ServerConnectionHandler handler = connectionRegistry.get(reciever);
if (handler != null) {
handler.sendData(sender, reciever, type, payload);
} else {
this.sendData(USER_NONE, userName, DATA_TYPE_ERROR, "Unknown User: " + reciever);
}
}
} else if (type.equals(DATA_TYPE_ERROR)) {
System.err.println("Received error from client (" + sender + "): " + payload);
} else {
System.err.println("Unknown data type received: " + type);
}
} catch(ChatProtocolException e) {
System.out.println("Error while processing data" + e.getMessage());
sendData(USER_NONE, userName, DATA_TYPE_ERROR, e.getMessage());
}
}
public void sendData(String sender, String receiver, String type, String payload) {
if (connection.isAvailable()) {
new StringBuilder();
String data = new StringBuilder()
.append(sender+"\n")
.append(receiver+"\n")
.append(type+"\n")
.append(payload+"\n")
.toString();
try {
connection.send(data);
} catch (SocketException e) {
System.out.println("Connection closed: " + e.getMessage());
} catch (EOFException e) {
System.out.println("Connection terminated by remote");
} catch(IOException e) {
System.out.println("Communication error: " + e.getMessage());
}
}
}
}