From 809d897da6b495a177ab2501d9e28759348062778e15bddca5edaba3d6d1e4ea Mon Sep 17 00:00:00 2001 From: AidarKC Date: Fri, 26 Dec 2025 13:12:53 +0300 Subject: [PATCH] =?UTF-8?q?26=2012=2025=20=D0=98=D1=81=D0=BF=D1=80=D0=B0?= =?UTF-8?q?=D0=B2=D0=B8=D0=BB=20=D0=B1=D0=B0=D0=B3=20=D1=87=D1=82=D0=BE=20?= =?UTF-8?q?=D0=BF=D0=B8=D1=80=20=D0=BF=D0=BE=D0=B2=D1=82=D0=BE=D1=80=D0=BD?= =?UTF-8?q?=D0=BE=D0=BC=20=D0=BF=D0=BE=D0=B4=D0=BA=D0=BB=D1=8E=D1=87=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B8=20=D0=BF=D0=BE=20=D0=BE=D0=B4=D0=BD=D0=BE?= =?UTF-8?q?=D0=B9=20=D1=81=D0=B5=D1=81=D1=81=D0=B8=D0=B8=20=D0=B2=D1=81?= =?UTF-8?q?=D1=91=20=D0=B1=D1=83=D0=B4=D0=B5=D1=82=20=D0=BD=D0=BE=D1=80?= =?UTF-8?q?=D0=BC=20=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D1=82=D1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../JSON/ActiveConnectionsRegistry.java | 85 +++++++++++-------- .../utils/NetExceptionResponseFactory.java | 14 ++- .../ws/Что недоделано. И потом надо доделать | 2 - 3 files changed, 62 insertions(+), 39 deletions(-) diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/ActiveConnectionsRegistry.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/ActiveConnectionsRegistry.java index da0e8d1..29c3bf6 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/ActiveConnectionsRegistry.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/ActiveConnectionsRegistry.java @@ -1,26 +1,19 @@ package server.logic.ws_protocol.JSON; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArraySet; /** * Реестр активных подключений (только авторизованные). - * - * Позволяет: - * - получить ConnectionContext по sessionId; - * - получить все активные подключения пользователя по login; - * - удалить подключение при закрытии WebSocket. - * - * найти все подключения пользователя: - * var set = ActiveConnectionsRegistry.getInstance().getByLoginId(loginId); - * - * найти конкретное подключение по sessionId: - * ConnectionContext ctx = ActiveConnectionsRegistry.getInstance().getBySessionId(sessionId); - * Session ws = ctx != null ? ctx.getWsSession() : null; */ public final class ActiveConnectionsRegistry { + private static final Logger log = LoggerFactory.getLogger(ActiveConnectionsRegistry.class); + private static final ActiveConnectionsRegistry INSTANCE = new ActiveConnectionsRegistry(); public static ActiveConnectionsRegistry getInstance() { @@ -47,15 +40,33 @@ public final class ActiveConnectionsRegistry { String sessionId = ctx.getSessionId(); String login = ctx.getLogin(); - if (sessionId == null || login == null || login.isBlank()) { + if (sessionId == null || sessionId.isBlank() || login == null || login.isBlank()) { + log.debug("register skipped: bad ctx fields (login='{}', sessionId='{}')", login, sessionId); return; } - bySessionId.put(sessionId, ctx); + // ✅ Если кто-то перерегистрировал тот же sessionId — вычищаем старый ctx из byLogin + ConnectionContext prev = bySessionId.put(sessionId, ctx); + if (prev != null && prev != ctx) { + String prevLogin = prev.getLogin(); + if (prevLogin != null && !prevLogin.isBlank()) { + Set prevSet = byLogin.get(prevLogin); + if (prevSet != null) { + prevSet.remove(prev); + if (prevSet.isEmpty()) { + byLogin.remove(prevLogin); + } + } + } + log.warn("sessionId reused: replaced previous ctx (sessionId={}, prevLogin={}, newLogin={})", + sessionId, prevLogin, login); + } byLogin .computeIfAbsent(login, id -> new CopyOnWriteArraySet<>()) .add(ctx); + + log.debug("registered ctx (login={}, sessionId={})", login, sessionId); } /** @@ -67,11 +78,17 @@ public final class ActiveConnectionsRegistry { String sessionId = ctx.getSessionId(); String login = ctx.getLogin(); - if (sessionId != null) { - bySessionId.remove(sessionId); + if (sessionId != null && !sessionId.isBlank()) { + ConnectionContext removed = bySessionId.remove(sessionId); + + // Если в мапе лежал другой ctx под тем же sessionId — не трогаем его byLogin + if (removed != null && removed != ctx) { + log.debug("remove(ctx): sessionId mapped to another ctx, skip byLogin cleanup (sessionId={})", sessionId); + return; + } } - if (login != null) { + if (login != null && !login.isBlank()) { Set set = byLogin.get(login); if (set != null) { set.remove(ctx); @@ -80,34 +97,38 @@ public final class ActiveConnectionsRegistry { } } } + + log.debug("removed ctx (login={}, sessionId={})", login, sessionId); } /** * Удалить подключение по sessionId. */ public void removeBySessionId(String sessionId) { - if (sessionId == null) return; + if (sessionId == null || sessionId.isBlank()) return; ConnectionContext ctx = bySessionId.remove(sessionId); - if (ctx != null) { - String login = ctx.getLogin(); - if (login != null) { - Set set = byLogin.get(login); - if (set != null) { - set.remove(ctx); - if (set.isEmpty()) { - byLogin.remove(login); - } + if (ctx == null) return; + + String login = ctx.getLogin(); + if (login != null && !login.isBlank()) { + Set set = byLogin.get(login); + if (set != null) { + set.remove(ctx); + if (set.isEmpty()) { + byLogin.remove(login); } } } + + log.debug("removed by sessionId (login={}, sessionId={})", login, sessionId); } /** * Получить контекст по sessionId. */ public ConnectionContext getBySessionId(String sessionId) { - if (sessionId == null) return null; + if (sessionId == null || sessionId.isBlank()) return null; return bySessionId.get(sessionId); } @@ -115,12 +136,8 @@ public final class ActiveConnectionsRegistry { * Получить все активные подключения пользователя по login. */ public Set getByLogin(String login) { - if (login == null) return Set.of(); + if (login == null || login.isBlank()) return Set.of(); Set set = byLogin.get(login); - if (set == null) { - return Set.of(); - } - // CopyOnWriteArraySet безопасно отдавать как есть - return set; + return (set == null) ? Set.of() : set; // CopyOnWriteArraySet можно отдавать как есть } } \ No newline at end of file diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/utils/NetExceptionResponseFactory.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/utils/NetExceptionResponseFactory.java index 7dbe4f3..fb38935 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/utils/NetExceptionResponseFactory.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/utils/NetExceptionResponseFactory.java @@ -19,8 +19,16 @@ public final class NetExceptionResponseFactory { String message) { Net_Exception_Response resp = new Net_Exception_Response(); - resp.setOp(req.getOp()); - resp.setRequestId(req.getRequestId()); + + // ✅ НЕ падаем, даже если req == null + if (req != null) { + resp.setOp(req.getOp()); + resp.setRequestId(req.getRequestId()); + } else { + resp.setOp(null); + resp.setRequestId(null); + } + resp.setStatus(status); resp.setCode(code); resp.setMessage(message); @@ -45,4 +53,4 @@ public final class NetExceptionResponseFactory { resp.setMessage(message); return resp; } -} +} \ No newline at end of file diff --git a/src/main/java/server/ws/Что недоделано. И потом надо доделать b/src/main/java/server/ws/Что недоделано. И потом надо доделать index 08ee935..a291c44 100644 --- a/src/main/java/server/ws/Что недоделано. И потом надо доделать +++ b/src/main/java/server/ws/Что недоделано. И потом надо доделать @@ -1,3 +1 @@ Работу с линиями - -Восстановление при подвисании сервера \ No newline at end of file