Merge branch 'revert-5e74b83a' into 'main'
Revert "Merge branch 'feat/client-network' into 'main'" See merge request cs108-fs26/Gruppe-13!37
This commit was merged in pull request #193.
This commit is contained in:
@@ -1,136 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client.chat;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message Object for internal handling of Chat-Messages
|
|
||||||
* TODO: Should be used on both sides of the network
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class Message {
|
|
||||||
private final MessageType type;
|
|
||||||
private final String message;
|
|
||||||
public String user;
|
|
||||||
public String timestamp;
|
|
||||||
public int game_id = 0;
|
|
||||||
public String target = null;
|
|
||||||
public enum MessageType {
|
|
||||||
GLOBAL, LOBBY, WHISPER
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for creating Messages with all information given
|
|
||||||
* @param type - Either global, local or whisper
|
|
||||||
* @param game_id - lobby id, or null, if the type is global
|
|
||||||
* @param user - username
|
|
||||||
* @param target - username of the target user, for whisper chat
|
|
||||||
* @param message
|
|
||||||
*/
|
|
||||||
|
|
||||||
public Message(MessageType type, int game_id, String user, String target, String timestamp, String message) {
|
|
||||||
this.type = type;
|
|
||||||
this.game_id = game_id;
|
|
||||||
this.user = user;
|
|
||||||
this.target = target;
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for creating the Messages of the current user, using this client -> time of writing is being recorded
|
|
||||||
* @param type - Either global, local or whisper
|
|
||||||
* @param game_id - lobby id, or null, if the type is global
|
|
||||||
* @param user - username
|
|
||||||
* @param target - username of the target user, for whisper chat
|
|
||||||
* @param message
|
|
||||||
*/
|
|
||||||
|
|
||||||
public Message(MessageType type, int game_id, String user, String target, String message) {
|
|
||||||
this.type = type;
|
|
||||||
this.game_id = game_id;
|
|
||||||
this.user = user;
|
|
||||||
this.target = target;
|
|
||||||
this.message = message;
|
|
||||||
LocalDateTime now = LocalDateTime.now();
|
|
||||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm");
|
|
||||||
this.timestamp = now.format(formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MessageType getMessageType() {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Method to test the system
|
|
||||||
* @return - String representation of the Message instance, as for example "player1: Hello World"
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return String.format("%s: %s", this.user, this.message);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/**
|
|
||||||
* Method to create the request representation of the message object, to be sent to the server
|
|
||||||
* @return - request as specified in the network protocol, as String
|
|
||||||
*/
|
|
||||||
public String toArgsString() {
|
|
||||||
return String.format("TYPE=%s GAME=%d USER=%s TARGET=%s TIME=%s TEXT=%s",
|
|
||||||
this.type.toString(), this.game_id, this.user, this.target, this.timestamp, this.message);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pattern, to analyze the response String with the given parameters
|
|
||||||
*/
|
|
||||||
public static Pattern msgRex = Pattern.compile(
|
|
||||||
"TYPE=(?<type>\\w+) GAME=(?<game>\\w+) USER=(?<user>\\w+) TARGET=(?<target>\\w+) TIME=(?<time>[0-9:.]+) TEXT=(?<text>.*)$");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method to create a Message Object, from the information given by the String
|
|
||||||
* @param response - String that got sent as a response from the server
|
|
||||||
* @return - New Message Object
|
|
||||||
*/
|
|
||||||
public static Message toMessage(String response) {
|
|
||||||
Matcher m = msgRex.matcher(response);
|
|
||||||
if (! m.matches()) {
|
|
||||||
throw new RuntimeException("Can not parse message: '" + response+"'");
|
|
||||||
}
|
|
||||||
String typeString=m.group("type");
|
|
||||||
|
|
||||||
switch (typeString) {
|
|
||||||
case "GLOBAL":
|
|
||||||
return new Message(MessageType.GLOBAL,
|
|
||||||
0,
|
|
||||||
m.group("user"),
|
|
||||||
null,
|
|
||||||
m.group("time"),
|
|
||||||
m.group("text"));
|
|
||||||
|
|
||||||
case "LOBBY":
|
|
||||||
return new Message(MessageType.LOBBY,
|
|
||||||
Integer.parseInt(m.group("game")),
|
|
||||||
m.group("user"),
|
|
||||||
null,
|
|
||||||
m.group("time"),
|
|
||||||
m.group("text"));
|
|
||||||
|
|
||||||
case "WHISPER":
|
|
||||||
return new Message(MessageType.WHISPER,
|
|
||||||
Integer.parseInt(m.group("game")),
|
|
||||||
m.group("user"),
|
|
||||||
m.group("target"),
|
|
||||||
m.group("time"),
|
|
||||||
m.group("text"));
|
|
||||||
|
|
||||||
default:
|
|
||||||
throw new RuntimeException("Unknown message type " + typeString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client.network;
|
|
||||||
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.ExecutorService;
|
|
||||||
import java.util.concurrent.Executors;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.client.chat.Message;
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket;
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TcpTransport;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responsible for the transferring of the data from the Client to the Server and the other way around
|
|
||||||
*/
|
|
||||||
|
|
||||||
public class ClientService {
|
|
||||||
|
|
||||||
private final TcpTransport clienttcptransport;
|
|
||||||
private final Socket socket;
|
|
||||||
|
|
||||||
private final String ip;
|
|
||||||
private final int port;
|
|
||||||
|
|
||||||
private final ExecutorService executor;
|
|
||||||
|
|
||||||
public static ArrayList<String> response;
|
|
||||||
private final AtomicInteger idGenerator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new ClientSession with a Socket, a Reader and Writer of the Input- and the Outputstream and a pool of threads to send requests and receive responses.
|
|
||||||
* @param ip : ip-adress of the server
|
|
||||||
* @param port : port of the server
|
|
||||||
*/
|
|
||||||
|
|
||||||
public ClientService(String ip, int port) {
|
|
||||||
|
|
||||||
this.ip = ip;
|
|
||||||
this.port = port;
|
|
||||||
this.idGenerator = new AtomicInteger(0);
|
|
||||||
|
|
||||||
try {
|
|
||||||
socket = new Socket(this.ip, this.port);
|
|
||||||
System.out.println("Connected");
|
|
||||||
clienttcptransport = new TcpTransport(socket);
|
|
||||||
}
|
|
||||||
catch (IOException i) {
|
|
||||||
throw new RuntimeException(i);
|
|
||||||
}
|
|
||||||
|
|
||||||
executor = Executors.newSingleThreadExecutor();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a ping to the server
|
|
||||||
* Returns nothing, the method processMessage() already handles the cases "+OK" or "-ERROR"
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void ping() {
|
|
||||||
processMessage("PING");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a login request to the server to create a new user on the server
|
|
||||||
* @param username
|
|
||||||
* @return - a new username, if the same username is already used by someone else
|
|
||||||
*/
|
|
||||||
|
|
||||||
public String login(String username) {
|
|
||||||
String msg = "LOGIN USERNAME=" + username;
|
|
||||||
return processMessage(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Send a Request to get the number of Messages currently in the Queue for the client.
|
|
||||||
* Then proceeds, if needed, to get the messages by sending
|
|
||||||
*/
|
|
||||||
|
|
||||||
public List<Message> getMessages() {
|
|
||||||
String countStr = processMessage("GET_MESSAGE_COUNT");
|
|
||||||
int count = Integer.parseInt(countStr);
|
|
||||||
System.out.println("Got " + count + " messages");
|
|
||||||
List<Message> messages = new ArrayList<>();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
String message = processMessage("GET_NEXT_MESSAGE");
|
|
||||||
if (message != null) {
|
|
||||||
Message message1 = Message.toMessage(message);
|
|
||||||
messages.add(message1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return messages;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends a Request to the Server containing all relevant information of the message the client wrote.
|
|
||||||
* @param message
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void sendMessage(Message message) {
|
|
||||||
String request = "SEND_MESSAGE " + message.toArgsString();
|
|
||||||
processMessage(request);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the Requests to the server and waits for the response
|
|
||||||
* If the response is "+OK" it proceeds normal
|
|
||||||
* If the response "-ERROR" it throws a runtime exception
|
|
||||||
* @param message
|
|
||||||
* @return - The response as a string, if it has to be returned
|
|
||||||
* (+OK will not be returned)
|
|
||||||
*/
|
|
||||||
|
|
||||||
private String processMessage(String message) {
|
|
||||||
AtomicReference<String> response = new AtomicReference<>();
|
|
||||||
sendRequest(() -> {
|
|
||||||
try {
|
|
||||||
writeToTransport(message);
|
|
||||||
String responseLine=null;
|
|
||||||
do {
|
|
||||||
responseLine = clienttcptransport.read().payload();
|
|
||||||
System.out.println("Raw message '" + responseLine + "'");
|
|
||||||
if ("+OK".equals(responseLine)) {
|
|
||||||
return;
|
|
||||||
} else if (("-ERROR").equals(responseLine)) {
|
|
||||||
throw new RuntimeException(responseLine);
|
|
||||||
}
|
|
||||||
response.set(responseLine);
|
|
||||||
} while(true);
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw getRuntimeException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return response.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param request
|
|
||||||
*/
|
|
||||||
|
|
||||||
private void sendRequest(Runnable request) {
|
|
||||||
Future<?> future = executor.submit(request);
|
|
||||||
try {
|
|
||||||
future.get();
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} catch (ExecutionException e) {
|
|
||||||
throw getRuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a Runtime Exceptions thrown by the sendRequest method
|
|
||||||
* @param e - an Exception
|
|
||||||
* @return - a Runtime Exception
|
|
||||||
*/
|
|
||||||
|
|
||||||
private static RuntimeException getRuntimeException(Exception e) {
|
|
||||||
Throwable reason = e.getCause();
|
|
||||||
RuntimeException re;
|
|
||||||
if (reason == null) {
|
|
||||||
reason = e;
|
|
||||||
} else if (reason instanceof RuntimeException rte) {
|
|
||||||
re = rte;
|
|
||||||
}
|
|
||||||
re = new RuntimeException(reason);
|
|
||||||
return re;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the Socket and shuts down the Threadpool associated with that Socket-Connection.
|
|
||||||
*/
|
|
||||||
|
|
||||||
public void closeSocket() {
|
|
||||||
try {
|
|
||||||
executor.shutdown();
|
|
||||||
clienttcptransport.close();
|
|
||||||
socket.close();
|
|
||||||
} catch (IOException j) {
|
|
||||||
System.out.println(j);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method to write with the tcp transport to the server
|
|
||||||
* @param s - Message to be sent
|
|
||||||
* @throws IOException
|
|
||||||
*/
|
|
||||||
|
|
||||||
private void writeToTransport(String s) throws IOException {
|
|
||||||
int id = this.idGenerator.incrementAndGet();
|
|
||||||
this.clienttcptransport.write(new RawPacket(id, s));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
|
|
||||||
public class Server {
|
|
||||||
private ServerSocket ss = null;
|
|
||||||
private BufferedReader input = null;
|
|
||||||
private BufferedWriter output = null;
|
|
||||||
|
|
||||||
public Server(int port) {
|
|
||||||
try {
|
|
||||||
ss = new ServerSocket(port);
|
|
||||||
System.out.println("Server started");
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
System.out.println(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void simpleListenLoop() {
|
|
||||||
System.out.println("Waiting for connection...");
|
|
||||||
Socket s = null;
|
|
||||||
try {
|
|
||||||
while (true) {
|
|
||||||
s = ss.accept();
|
|
||||||
System.out.println("Accepted connection");
|
|
||||||
|
|
||||||
input = new BufferedReader(new InputStreamReader(s.getInputStream()));
|
|
||||||
|
|
||||||
output = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
|
|
||||||
String line = null;
|
|
||||||
while((line = input.readLine())!=null) {
|
|
||||||
System.out.println("Server got "+ line);
|
|
||||||
// do something with this line
|
|
||||||
output.write("OK"+"\n");
|
|
||||||
output.flush();
|
|
||||||
if(Thread.currentThread().isInterrupted()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(Thread.currentThread().isInterrupted()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
s.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -25,10 +25,6 @@ public class ServerApp {
|
|||||||
Logger logger = LogManager.getLogger(ServerApp.class);
|
Logger logger = LogManager.getLogger(ServerApp.class);
|
||||||
logger.info("Starting server at port {}", port);
|
logger.info("Starting server at port {}", port);
|
||||||
|
|
||||||
startServerCore(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void startServerCore(int port) {
|
|
||||||
EventBus eventBus = new EventBus();
|
EventBus eventBus = new EventBus();
|
||||||
SessionManager sessionManager = new SessionManager();
|
SessionManager sessionManager = new SessionManager();
|
||||||
eventBus.subscribe(
|
eventBus.subscribe(
|
||||||
|
|||||||
-15
@@ -1,15 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.events;
|
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.client.chat.Message;
|
|
||||||
|
|
||||||
public class ChatMessageEvent implements Event {
|
|
||||||
private final Message message;
|
|
||||||
|
|
||||||
public ChatMessageEvent(Message message) {
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Message getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-56
@@ -1,56 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.handlers;
|
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.client.chat.Message;
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.ChatMessageEvent;
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
|
||||||
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
|
|
||||||
public class ChatHandler {
|
|
||||||
private final EventBus eventBus;
|
|
||||||
private final ArrayDeque<Message> receivedMessages = new ArrayDeque<>();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a ChatHandler for a Session
|
|
||||||
* @param eventBus
|
|
||||||
*/
|
|
||||||
public ChatHandler(EventBus eventBus) {
|
|
||||||
eventBus.subscribe(ChatMessageEvent.class, (event) -> {
|
|
||||||
processMessageEvent(event.getMessage());
|
|
||||||
});
|
|
||||||
this.eventBus = eventBus;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sends the Chat Message that the server received to the queue
|
|
||||||
* @param message
|
|
||||||
*/
|
|
||||||
|
|
||||||
private synchronized void processMessageEvent(Message message) {
|
|
||||||
// TODO: filter for Messages that were sent by the Client
|
|
||||||
this.receivedMessages.offer(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current size of the queue (Used for Command GET_MESSAGE_COUNT)
|
|
||||||
*/
|
|
||||||
public synchronized int getMessageCount() {
|
|
||||||
return this.receivedMessages.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Takes the next message out of the queue, to be sent to the Client
|
|
||||||
*/
|
|
||||||
public synchronized Message getNextMessage() {
|
|
||||||
return this.receivedMessages.poll();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Message is converted into a String and sent to the Client
|
|
||||||
*/
|
|
||||||
public void sendMessage(String messageString) {
|
|
||||||
Message msg = Message.toMessage(messageString);
|
|
||||||
this.eventBus.publish(new ChatMessageEvent(msg));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-57
@@ -1,57 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.handlers;
|
|
||||||
|
|
||||||
import jdk.jshell.spi.ExecutionControl;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.regex.Matcher;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
public class UserHandler {
|
|
||||||
|
|
||||||
private final List<String> usernames;
|
|
||||||
|
|
||||||
public UserHandler() {
|
|
||||||
this.usernames = new ArrayList<String>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Boolean checkUsername(String username) {
|
|
||||||
for (int i = 0; i < this.usernames.size(); i++) {
|
|
||||||
if (this.usernames.get(i).equals(username)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Pattern usrRex = Pattern.compile("USERNAME=(?<user>\\w+)");
|
|
||||||
|
|
||||||
public String addUsername(String username) {
|
|
||||||
Matcher matcher = usrRex.matcher(username);
|
|
||||||
if (! matcher.find()) {
|
|
||||||
throw new RuntimeException("Cannot parse " + username);
|
|
||||||
}
|
|
||||||
String usr = matcher.group("user");
|
|
||||||
if (!this.checkUsername(usr)) {
|
|
||||||
this.usernames.add(usr);
|
|
||||||
} else {
|
|
||||||
this.usernames.add(getAltUsername(usr));
|
|
||||||
}
|
|
||||||
return String.format("USERNAME=%s", usr);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeUsername(String username) {
|
|
||||||
this.usernames.remove(username);
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getAltUsername(String username) {
|
|
||||||
int count = 0;
|
|
||||||
while (true) {
|
|
||||||
username = String.format("%s%d", username, ++count);
|
|
||||||
if (this.checkUsername(username)) {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
+14
-69
@@ -1,32 +1,24 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
||||||
|
|
||||||
import java.io.EOFException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.handlers.ChatHandler;
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.handlers.UserHandler;
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket;
|
|
||||||
import org.apache.logging.log4j.LogManager;
|
|
||||||
import org.apache.logging.log4j.Logger;
|
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.PrimitiveRequest;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.parser.ProtocolParser;
|
||||||
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket;
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
|
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.apache.logging.log4j.LogManager;
|
||||||
|
import org.apache.logging.log4j.Logger;
|
||||||
|
|
||||||
/**
|
/** Represents a client session in the network server. */
|
||||||
* Represents a client session in the network server.
|
|
||||||
*/
|
|
||||||
public class Session implements Runnable {
|
public class Session implements Runnable {
|
||||||
private final ChatHandler chatHandler;
|
|
||||||
private final UserHandler userHandler;
|
|
||||||
private SessionId id;
|
private SessionId id;
|
||||||
private Thread thread;
|
private Thread thread;
|
||||||
private TransportLayer transport;
|
private TransportLayer transport;
|
||||||
private Logger logger;
|
private Logger logger;
|
||||||
private Boolean running;
|
private Boolean running;
|
||||||
private EventBus eventBus;
|
private EventBus eventBus;
|
||||||
private AtomicInteger idGenerator;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Session with the given transport and event bus.
|
* Creates a new Session with the given transport and event bus.
|
||||||
@@ -44,9 +36,6 @@ public class Session implements Runnable {
|
|||||||
|
|
||||||
this.logger = LogManager.getLogger(Session.class.toString() + id.value());
|
this.logger = LogManager.getLogger(Session.class.toString() + id.value());
|
||||||
this.logger.info("Created new session");
|
this.logger.info("Created new session");
|
||||||
this.idGenerator = new AtomicInteger();
|
|
||||||
this.chatHandler = new ChatHandler(eventBus);
|
|
||||||
this.userHandler = new UserHandler();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -58,9 +47,7 @@ public class Session implements Runnable {
|
|||||||
return this.id;
|
return this.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Starts the session thread. */
|
||||||
* Starts the session thread.
|
|
||||||
*/
|
|
||||||
public void start() {
|
public void start() {
|
||||||
thread.start();
|
thread.start();
|
||||||
}
|
}
|
||||||
@@ -75,53 +62,16 @@ public class Session implements Runnable {
|
|||||||
this.running = false;
|
this.running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Runs the session loop, reading from the transport. */
|
||||||
* Runs the session loop, reading from the transport.
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
while (running) {
|
while (running) {
|
||||||
try {
|
try {
|
||||||
String clientCommand = transport.read().payload();
|
RawPacket rawPacket = transport.read();
|
||||||
String[] commandAndArgs = clientCommand.split("\\s+", 2);
|
logger.debug("Recieved: {}", rawPacket);
|
||||||
String commandWord = commandAndArgs[0];
|
|
||||||
System.out.println("Session "+id.value()+" Received: " + commandAndArgs[0]);
|
|
||||||
switch (commandWord) {
|
|
||||||
case "SEND_MESSAGE":
|
|
||||||
this.chatHandler.sendMessage(commandAndArgs[1]);
|
|
||||||
writeToTransport("+OK");
|
|
||||||
writeToTransport("+OK");
|
|
||||||
break;
|
|
||||||
case "GET_MESSAGE_COUNT":
|
|
||||||
int count = chatHandler.getMessageCount();
|
|
||||||
writeToTransport(""+count);
|
|
||||||
writeToTransport("+OK");
|
|
||||||
break;
|
|
||||||
case "GET_NEXT_MESSAGE":
|
|
||||||
if (chatHandler.getMessageCount() == 0) {
|
|
||||||
logger.warn("FAIL No more messages!");
|
|
||||||
writeToTransport("-ERROR");
|
|
||||||
} else {
|
|
||||||
String msgString = chatHandler.getNextMessage().toArgsString();
|
|
||||||
logger.debug("Session "+id.value()+" Write next message "+msgString);
|
|
||||||
writeToTransport(msgString);
|
|
||||||
writeToTransport("+OK");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "LOGIN":
|
|
||||||
String usr = userHandler.addUsername(commandAndArgs[1]);
|
|
||||||
writeToTransport(usr);
|
|
||||||
writeToTransport("+OK");
|
|
||||||
|
|
||||||
case "LOGOUT":
|
PrimitiveRequest primitiveRequest = ProtocolParser.parse(rawPacket);
|
||||||
userHandler.removeUsername(commandAndArgs[1]);
|
logger.debug("Parsed request to {}", primitiveRequest);
|
||||||
writeToTransport("+OK");
|
|
||||||
case "PING":
|
|
||||||
|
|
||||||
default:
|
|
||||||
this.logger.warn("Unknown command: " + commandWord);
|
|
||||||
writeToTransport("-ERROR");
|
|
||||||
}
|
|
||||||
} catch (EOFException e) {
|
} catch (EOFException e) {
|
||||||
logger.info("Client disconnected");
|
logger.info("Client disconnected");
|
||||||
eventBus.publish(new DisconnectEvent(id));
|
eventBus.publish(new DisconnectEvent(id));
|
||||||
@@ -132,9 +82,4 @@ public class Session implements Runnable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeToTransport(String s) throws IOException {
|
|
||||||
int id = this.idGenerator.incrementAndGet();
|
|
||||||
this.transport.write(new RawPacket(id, s));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-3
@@ -8,8 +8,8 @@ import java.nio.charset.StandardCharsets;
|
|||||||
|
|
||||||
/** Implements TCP-based transport layer for network communication. */
|
/** Implements TCP-based transport layer for network communication. */
|
||||||
public class TcpTransport implements TransportLayer {
|
public class TcpTransport implements TransportLayer {
|
||||||
private final Socket socket;
|
private Socket socket;
|
||||||
private final DataInputStream in;
|
private DataInputStream in;
|
||||||
private DataOutputStream out;
|
private DataOutputStream out;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -49,8 +49,9 @@ public class TcpTransport implements TransportLayer {
|
|||||||
public void write(RawPacket data) throws IOException {
|
public void write(RawPacket data) throws IOException {
|
||||||
int requestId = data.requestId();
|
int requestId = data.requestId();
|
||||||
byte[] rawPayload = data.payload().getBytes(StandardCharsets.UTF_8);
|
byte[] rawPayload = data.payload().getBytes(StandardCharsets.UTF_8);
|
||||||
out.writeInt(rawPayload.length);
|
|
||||||
out.writeInt(requestId);
|
out.writeInt(requestId);
|
||||||
|
out.writeInt(rawPayload.length);
|
||||||
out.write(rawPayload);
|
out.write(rawPayload);
|
||||||
out.flush();
|
out.flush();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
package ch.unibas.dmi.dbis.cs108.casono.client.network;
|
|
||||||
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.client.chat.Message;
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.Server;
|
|
||||||
import ch.unibas.dmi.dbis.cs108.casono.server.ServerApp;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
||||||
|
|
||||||
public class ClientServiceTest {
|
|
||||||
|
|
||||||
Server server = null;
|
|
||||||
private Thread serverThread;
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void setUp() {
|
|
||||||
//server = new Server(5000);
|
|
||||||
//serverThread = new Thread(()-> server.simpleListenLoop());
|
|
||||||
//serverThread.start();
|
|
||||||
ServerApp.startServerCore(5000);
|
|
||||||
}
|
|
||||||
@AfterEach
|
|
||||||
public void tearDown() {
|
|
||||||
|
|
||||||
//serverThread.interrupt();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testClientService() throws ExecutionException, InterruptedException {
|
|
||||||
ClientService client1 = new ClientService("localhost", 5000);
|
|
||||||
/*
|
|
||||||
ClientService client2 = new ClientService("localhost", 5000);
|
|
||||||
Thread.sleep(2000);
|
|
||||||
client1.sendMessage(new Message(Message.MessageType.GLOBAL, 0, "Mathis", null, "blahh"));
|
|
||||||
Thread.sleep(5000);
|
|
||||||
List<Message> received = client2.getMessages();
|
|
||||||
|
|
||||||
assertEquals(1, received.size());
|
|
||||||
Message s = received.get(0);
|
|
||||||
assertEquals(Message.MessageType.GLOBAL, s.getMessageType());
|
|
||||||
*/
|
|
||||||
|
|
||||||
String username = client1.login("Mathis");
|
|
||||||
assertEquals("USERNAME=Mathis", username);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMessage() {
|
|
||||||
Message msg = new Message(Message.MessageType.GLOBAL, 0, "Mathis", null, "blah");
|
|
||||||
Message msg2 = new Message(Message.MessageType.WHISPER, 1, "Mathis", "Julian", "blah");
|
|
||||||
String serial = msg.toArgsString();
|
|
||||||
Message roundTripped = Message.toMessage(serial);
|
|
||||||
String serial2 = msg2.toArgsString();
|
|
||||||
Message roundTripped2 = Message.toMessage(serial2);
|
|
||||||
|
|
||||||
assert(roundTripped.game_id == msg.game_id);
|
|
||||||
assertEquals(0, roundTripped.game_id);
|
|
||||||
assert(roundTripped2.game_id == msg2.game_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user