Add (v1) NetworkManager, Session and Transport #173
@@ -14,7 +14,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TcpTransport;
|
||||
|
||||
/**
|
||||
* Creates and manages the server socket. Accepts new incomming connections and creates sessions.
|
||||
* Creates and manages the server socket. Accepts new incoming connections and creates sessions.
|
||||
*/
|
||||
public class NetworkManager implements Runnable {
|
||||
private Integer port;
|
||||
@@ -24,6 +24,13 @@ public class NetworkManager implements Runnable {
|
||||
private SessionManager sessionManager;
|
||||
private EventBus eventBus;
|
||||
|
||||
/**
|
||||
* Creates a new NetworkManager with the given port, session manager, and event bus.
|
||||
*
|
||||
* @param port the port to listen on
|
||||
* @param sessionManager the session manager to use
|
||||
* @param eventBus the event bus for events
|
||||
*/
|
||||
public NetworkManager(Integer port, SessionManager sessionManager, EventBus eventBus) {
|
||||
this.port = port;
|
||||
this.logger = LogManager.getLogger(NetworkManager.class);
|
||||
@@ -34,17 +41,26 @@ public class NetworkManager implements Runnable {
|
||||
this.eventBus.subscribe(DisconnectEvent.class, event -> clientDisconnected(event));
|
||||
}
|
||||
|
||||
/* Starts the internal thread to accept new connections.
|
||||
/**
|
||||
* Starts the internal thread to accept new connections.
|
||||
*/
|
||||
public void start() {
|
||||
logger.debug("Starting server at port " + port);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles client disconnection events.
|
||||
*
|
||||
* @param event the disconnect event
|
||||
*/
|
||||
public void clientDisconnected(DisconnectEvent event) {
|
||||
logger.info("Session " + event.sessionId().value() + " disconnected adhasghd");
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the network manager loop, accepting connections.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
try (ServerSocket serverSocket = new ServerSocket(port)) {
|
||||
|
||||
+3
@@ -2,4 +2,7 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.events;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionId;
|
||||
|
||||
/**
|
||||
* Represents a disconnect event for a session.
|
||||
*/
|
||||
public record DisconnectEvent(SessionId sessionId) implements Event {}
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.events;
|
||||
|
||||
/**
|
||||
* Marker interface for events in the event bus system.
|
||||
*/
|
||||
interface Event {}
|
||||
|
||||
@@ -7,15 +7,29 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A simple event bus for publishing and subscribing to events.
|
||||
*/
|
||||
public class EventBus {
|
||||
private final Map<Class<?>, List<Consumer<Object>>> handlers = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Subscribes a handler to a specific event type.
|
||||
*
|
||||
* @param eventType the class of the event to subscribe to
|
||||
* @param handler the consumer to handle the event
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // This cast is safe, because handlers only get passed the type they subscribed to
|
||||
public <T extends Event> void subscribe(Class<T> eventType, Consumer<T> handler) {
|
||||
handlers.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>())
|
||||
.add((Consumer<Object>) (Consumer<?>) handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Publishes an event to all subscribed handlers.
|
||||
*
|
||||
* @param event the event to publish
|
||||
*/
|
||||
public <T extends Event> void publish(T event) {
|
||||
Objects.requireNonNull(event, "event must not be null");
|
||||
List<Consumer<Object>> subscribers = handlers.get(event.getClass());
|
||||
|
||||
@@ -9,6 +9,9 @@ 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.transport.TransportLayer;
|
||||
|
||||
/**
|
||||
* Represents a client session in the network server.
|
||||
*/
|
||||
public class Session implements Runnable {
|
||||
private SessionId id;
|
||||
private Thread thread;
|
||||
@@ -17,6 +20,13 @@ public class Session implements Runnable {
|
||||
private Boolean running;
|
||||
private EventBus eventBus;
|
||||
|
||||
/**
|
||||
* Creates a new Session with the given transport and event bus.
|
||||
*
|
||||
* @param transport the transport layer for communication
|
||||
* @param eventBus the event bus for publishing events
|
||||
* @throws IOException if an I/O error occurs during initialization
|
||||
*/
|
||||
public Session(TransportLayer transport, EventBus eventBus) throws IOException {
|
||||
this.id = new SessionId();
|
||||
this.thread = new Thread(this, "session-" + this.id.value());
|
||||
@@ -28,19 +38,35 @@ public class Session implements Runnable {
|
||||
this.logger.info("Created new session");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the ID of this session.
|
||||
*
|
||||
* @return the session ID
|
||||
*/
|
||||
public SessionId getId() {
|
||||
return this.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the session thread.
|
||||
*/
|
||||
public void start() {
|
||||
thread.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the session and its transport.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
transport.close();
|
||||
this.running = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the session loop, reading from the transport.
|
||||
*/
|
||||
@Override
|
||||
public void run() {
|
||||
while (running) {
|
||||
|
||||
+15
-2
@@ -2,20 +2,33 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class SessionId {
|
||||
/**
|
||||
* The SessionId is used to identify a unique client connection in the SessionRegistry
|
||||
* Represents a unique identifier for a session.
|
||||
*/
|
||||
public class SessionId {
|
||||
private final UUID value;
|
||||
|
||||
/**
|
||||
* Creates a new SessionId with a randomly generated UUID.
|
||||
*/
|
||||
public SessionId() {
|
||||
this.value = UUID.randomUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new SessionId with the specified UUID.
|
||||
*
|
||||
* @param UUID to use for this SessionId
|
||||
*/
|
||||
public SessionId(UUID value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the UUID value of this SessionId.
|
||||
*
|
||||
* @return the UUID value
|
||||
*/
|
||||
public UUID value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
+29
@@ -3,27 +3,56 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.sessions;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Manages active sessions in the server.
|
||||
*/
|
||||
public class SessionManager {
|
||||
private Map<SessionId, Session> sessions;
|
||||
|
||||
/**
|
||||
* Constructs a new SessionManager.
|
||||
*/
|
||||
public SessionManager() {
|
||||
this.sessions = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a session to the manager.
|
||||
*
|
||||
* @param session the session to add
|
||||
*/
|
||||
public void addSession(Session session) {
|
||||
sessions.put(session.getId(), session);
|
||||
System.out.println("Added session " + session.getId().value() + " to session manager");
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a session by its ID.
|
||||
*
|
||||
* @param id the ID of the session to remove
|
||||
* @return the removed session, or null if not found
|
||||
*/
|
||||
public Session removeSession(SessionId id) {
|
||||
System.out.println("Removed session " + id.value() + " from session manager");
|
||||
return sessions.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the specified session.
|
||||
*
|
||||
* @param session the session to remove
|
||||
* @return the removed session, or null if not found
|
||||
*/
|
||||
public Session removeSession(Session session) {
|
||||
return sessions.remove(session.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a session by its ID.
|
||||
*
|
||||
* @param id the ID of the session to retrieve
|
||||
* @return the session with the specified ID, or null if not found
|
||||
*/
|
||||
public Session getSessionById(SessionId id) {
|
||||
return sessions.get(id);
|
||||
}
|
||||
|
||||
+26
@@ -6,17 +6,32 @@ import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
/**
|
||||
* Implements TCP-based transport layer for network communication.
|
||||
*/
|
||||
public class TcpTransport implements TransportLayer {
|
||||
private Socket socket;
|
||||
private DataInputStream in;
|
||||
private DataOutputStream out;
|
||||
|
||||
/**
|
||||
* Creates a new TcpTransport with the given socket.
|
||||
*
|
||||
* @param socket the socket to use for communication
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public TcpTransport(Socket socket) throws IOException {
|
||||
this.socket = socket;
|
||||
this.in = new DataInputStream(socket.getInputStream());
|
||||
this.out = new DataOutputStream(socket.getOutputStream());
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a string from the socket.
|
||||
*
|
||||
* @return the read string
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public String read() throws IOException {
|
||||
int length = in.readInt();
|
||||
byte[] payload = new byte[length];
|
||||
@@ -24,6 +39,12 @@ public class TcpTransport implements TransportLayer {
|
||||
return new String(payload, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to the socket.
|
||||
*
|
||||
* @param payload the string to write
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void write(String payload) throws IOException {
|
||||
byte[] rawPayload = payload.getBytes(StandardCharsets.UTF_8);
|
||||
out.writeInt(rawPayload.length);
|
||||
@@ -31,6 +52,11 @@ public class TcpTransport implements TransportLayer {
|
||||
out.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the socket.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
public void close() throws IOException {
|
||||
socket.close();
|
||||
}
|
||||
|
||||
+22
@@ -2,8 +2,30 @@ package ch.unibas.dmi.dbis.cs108.casono.server.network.transport;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Interface for transport layer implementations.
|
||||
*/
|
||||
public interface TransportLayer {
|
||||
/**
|
||||
* Reads data from the transport layer.
|
||||
*
|
||||
* @return the read data as a string
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
String read() throws IOException;
|
||||
|
||||
/**
|
||||
* Writes data to the transport layer.
|
||||
*
|
||||
* @param data the data to write
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
void write(String data) throws IOException;
|
||||
|
||||
/**
|
||||
* Closes the transport layer.
|
||||
*
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
void close() throws IOException;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user