zhuyifan

fix

...@@ -7,14 +7,17 @@ import com.infoloop.tianting.server.session.UserSessionData; ...@@ -7,14 +7,17 @@ import com.infoloop.tianting.server.session.UserSessionData;
7 import com.infoloop.tianting.server.session.UserSessionKey; 7 import com.infoloop.tianting.server.session.UserSessionKey;
8 import com.infoloop.tianting.server.session.UserTypeEnum; 8 import com.infoloop.tianting.server.session.UserTypeEnum;
9 import lombok.extern.slf4j.Slf4j; 9 import lombok.extern.slf4j.Slf4j;
10 +import org.apache.commons.lang3.StringUtils;
10 import org.springframework.web.socket.CloseStatus; 11 import org.springframework.web.socket.CloseStatus;
11 import org.springframework.web.socket.TextMessage; 12 import org.springframework.web.socket.TextMessage;
12 import org.springframework.web.socket.WebSocketSession; 13 import org.springframework.web.socket.WebSocketSession;
13 import org.springframework.web.socket.handler.TextWebSocketHandler; 14 import org.springframework.web.socket.handler.TextWebSocketHandler;
14 15
16 +import javax.annotation.Nullable;
15 import java.io.IOException; 17 import java.io.IOException;
16 import java.util.List; 18 import java.util.List;
17 import java.util.concurrent.ConcurrentHashMap; 19 import java.util.concurrent.ConcurrentHashMap;
20 +import java.util.concurrent.CopyOnWriteArrayList;
18 import java.util.concurrent.Executors; 21 import java.util.concurrent.Executors;
19 import java.util.concurrent.ScheduledExecutorService; 22 import java.util.concurrent.ScheduledExecutorService;
20 import java.util.concurrent.TimeUnit; 23 import java.util.concurrent.TimeUnit;
...@@ -23,7 +26,7 @@ import java.util.stream.Collectors; ...@@ -23,7 +26,7 @@ import java.util.stream.Collectors;
23 @Slf4j 26 @Slf4j
24 public class WebSocketServer extends TextWebSocketHandler { 27 public class WebSocketServer extends TextWebSocketHandler {
25 28
26 - private static final ConcurrentHashMap<UserSessionKey, UserSessionData> userSessions = new ConcurrentHashMap<>(); 29 + private static final ConcurrentHashMap<UserSessionKey, List<UserSessionData>> userSessions = new ConcurrentHashMap<>();
27 private static final long INACTIVITY_TIMEOUT = 60 * 60 * 1000; 30 private static final long INACTIVITY_TIMEOUT = 60 * 60 * 1000;
28 private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); 31 private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
29 32
...@@ -33,38 +36,47 @@ public class WebSocketServer extends TextWebSocketHandler { ...@@ -33,38 +36,47 @@ public class WebSocketServer extends TextWebSocketHandler {
33 36
34 private static void cleanInactiveSessions() { 37 private static void cleanInactiveSessions() {
35 final var currentTime = System.currentTimeMillis(); 38 final var currentTime = System.currentTimeMillis();
36 - final var iterator = userSessions.entrySet().iterator(); 39 + userSessions.forEach((key, sessionList) ->
37 - log.info("cleanInactiveSessions; sessions:{}", userSessions.keySet()); 40 + userSessions.computeIfPresent(key, (k, sessions) -> {
38 - while (iterator.hasNext()) { 41 + sessions.removeIf(userData -> {
39 - final var entry = iterator.next(); 42 + if (currentTime - userData.getLastActiveTime() > INACTIVITY_TIMEOUT) {
40 - final var key = entry.getKey(); 43 + try {
41 - final var userData = entry.getValue(); 44 + userData.closeSession();
42 - final var lastActiveTime = userData.getLastActiveTime(); 45 + log.info("closeSession, userId: {}, userType: {}", key.getUserId(), key.getUserType());
43 - if (currentTime - lastActiveTime > INACTIVITY_TIMEOUT) { 46 + } catch (IOException e) {
44 - try { 47 + log.error("closeSession error, userId: {}, userType: {}", key.getUserId(), key.getUserType(), e);
45 - userData.closeSession(); 48 + }
46 - log.info("closeSession,userId : {}, userType : {}", key.getUserId(), key.getUserType()); 49 + return true;
47 - } catch (IOException e) { 50 + }
48 - log.error("closeSession error,userId : {}, userType : {}", key.getUserId(), key.getUserType(), e); 51 + return false;
49 - } 52 + });
50 - iterator.remove(); 53 + return sessions.isEmpty() ? null : sessions; // 为空时移除
51 - } 54 + })
52 - } 55 + );
53 } 56 }
54 57
58 +
55 public static <T> void sendMessageToUser(UserSessionKey key, SocketMessage<T> socketMessage) throws IOException { 59 public static <T> void sendMessageToUser(UserSessionKey key, SocketMessage<T> socketMessage) throws IOException {
56 sendMessageToUsers(List.of(key), socketMessage); 60 sendMessageToUsers(List.of(key), socketMessage);
57 } 61 }
58 62
59 public static <T> void sendMessageToUsers(List<UserSessionKey> keys, SocketMessage<T> socketMessage) throws IOException { 63 public static <T> void sendMessageToUsers(List<UserSessionKey> keys, SocketMessage<T> socketMessage) throws IOException {
60 for (final var key : keys) { 64 for (final var key : keys) {
61 - final var userData = userSessions.get(key); 65 + final var sessionList = userSessions.get(key);
62 - if (userData != null && userData.getSession().isOpen()) { 66 + if (sessionList != null) {
63 - userData.getSession().sendMessage(new TextMessage(socketMessage.toJsonString())); 67 + sessionList.forEach(userData -> {
64 - userData.updateLastActiveTime(); 68 + try {
65 - log.info("send message to userId : {}, userType : {}", key.getUserId(), key.getUserType()); 69 + if (userData.getSession().isOpen()) {
66 - } else { 70 + userData.getSession().sendMessage(new TextMessage(socketMessage.toJsonString()));
67 - log.info("userId : {}, userType : {} WebSocket connect closed; ", key.getUserId(), key.getUserType()); 71 + userData.updateLastActiveTime();
72 + log.info("Send message to userId: {}, userType: {}", key.getUserId(), key.getUserType());
73 + } else {
74 + log.info("WebSocket closed; userId: {}, userType: {}", key.getUserId(), key.getUserType());
75 + }
76 + } catch (IOException e) {
77 + log.error("Failed to send message; userId: {}, userType: {}", key.getUserId(), key.getUserType(), e);
78 + }
79 + });
68 } 80 }
69 } 81 }
70 } 82 }
...@@ -75,39 +87,40 @@ public class WebSocketServer extends TextWebSocketHandler { ...@@ -75,39 +87,40 @@ public class WebSocketServer extends TextWebSocketHandler {
75 87
76 public static <T> void sendMessageByUserTypes(List<UserTypeEnum> userTypes, SocketMessage<T> socketMessage) throws IOException { 88 public static <T> void sendMessageByUserTypes(List<UserTypeEnum> userTypes, SocketMessage<T> socketMessage) throws IOException {
77 final var keys = userSessions.keySet().stream() 89 final var keys = userSessions.keySet().stream()
78 - .filter(userSessionData -> userTypes.contains(userSessionData.getUserType())) 90 + .filter(userKey -> userTypes.contains(userKey.getUserType()))
79 .collect(Collectors.toList()); 91 .collect(Collectors.toList());
80 sendMessageToUsers(keys, socketMessage); 92 sendMessageToUsers(keys, socketMessage);
81 } 93 }
82 94
83 public static <T> void sendMessageByUserTypes(List<UserTypeEnum> userTypes, List<UserAuthData> userAuthData, SocketMessage<T> socketMessage) throws IOException { 95 public static <T> void sendMessageByUserTypes(List<UserTypeEnum> userTypes, List<UserAuthData> userAuthData, SocketMessage<T> socketMessage) throws IOException {
84 final var keys = userSessions.keySet().stream() 96 final var keys = userSessions.keySet().stream()
85 - .filter(userSessionData -> userTypes.contains(userSessionData.getUserType())) 97 + .filter(userKey -> userTypes.contains(userKey.getUserType()))
86 - .filter(userSessionData -> userAuthData.isEmpty() || (userAuthData.stream().anyMatch(data -> String.valueOf(data.getOperatorId()).equals(userSessionData.getUserId()) && data.getUserType().equals(userSessionData.getUserType())))) 98 + .filter(userKey -> userAuthData.isEmpty()
99 + || userAuthData.stream().anyMatch(data -> String.valueOf(data.getOperatorId()).equals(userKey.getUserId()) && data.getUserType().equals(userKey.getUserType())))
87 .collect(Collectors.toList()); 100 .collect(Collectors.toList());
88 sendMessageToUsers(keys, socketMessage); 101 sendMessageToUsers(keys, socketMessage);
89 } 102 }
90 103
91 public static void closeConnection(UserSessionKey key) { 104 public static void closeConnection(UserSessionKey key) {
92 - final var userData = userSessions.get(key); 105 + userSessions.computeIfPresent(key, (k, sessionList) -> {
93 - if (userData != null) { 106 + sessionList.forEach(userData -> {
94 - try { 107 + try {
95 - userData.closeSession(); 108 + userData.closeSession();
96 - userSessions.remove(key); 109 + } catch (IOException e) {
97 - log.info("WebSocket close; userId = {}, userType = {}", key.getUserId(), key.getUserType()); 110 + log.error("WebSocket close failed: userId: {}, userType: {}", key.getUserId(), key.getUserType(), e);
98 - } catch (IOException e) { 111 + }
99 - log.error("WebSocket close failed: userId = {}, userType = {}", key.getUserId(), key.getUserType(), e); 112 + });
100 - } 113 + return null; // 直接返回 null,移除 key
101 - } else { 114 + });
102 - log.warn("Not Found userId : {}, userType : {} WebSocket Connect ", key.getUserId(), key.getUserType()); 115 + log.info("WebSocket closed; userId: {}, userType: {}", key.getUserId(), key.getUserType());
103 - }
104 } 116 }
105 117
118 + @Nullable
106 private UserSessionKey getUserSessionKey(WebSocketSession session) { 119 private UserSessionKey getUserSessionKey(WebSocketSession session) {
107 final var userId = (String) session.getAttributes().get(CommonConstants.USER_ID); 120 final var userId = (String) session.getAttributes().get(CommonConstants.USER_ID);
108 final var userTypeStr = (String) session.getAttributes().get(CommonConstants.USER_TYPE); 121 final var userTypeStr = (String) session.getAttributes().get(CommonConstants.USER_TYPE);
109 final var userType = UserTypeEnum.fromString(userTypeStr); 122 final var userType = UserTypeEnum.fromString(userTypeStr);
110 - if (userId != null && userType != null) { 123 + if (StringUtils.isNotEmpty(userId) && userType != null) {
111 return UserSessionKey.builder().userId(userId).userType(userType).build(); 124 return UserSessionKey.builder().userId(userId).userType(userType).build();
112 } 125 }
113 return null; 126 return null;
...@@ -117,30 +130,25 @@ public class WebSocketServer extends TextWebSocketHandler { ...@@ -117,30 +130,25 @@ public class WebSocketServer extends TextWebSocketHandler {
117 public void afterConnectionEstablished(WebSocketSession session) { 130 public void afterConnectionEstablished(WebSocketSession session) {
118 final var key = getUserSessionKey(session); 131 final var key = getUserSessionKey(session);
119 if (key != null) { 132 if (key != null) {
120 - final var existingSession = userSessions.remove(key); 133 + userSessions.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>()).add(new UserSessionData(session));
121 - if (existingSession != null) { 134 + log.info("WebSocket connection established; userId: {}, userType: {}", key.getUserId(), key.getUserType());
122 - try {
123 - existingSession.closeSession();
124 - log.info("WebSocket old connect closed,userId : {}, userType : {}", key.getUserId(), key.getUserType());
125 - } catch (IOException e) {
126 - log.error("old WebSocket close failed,userId : {}, userType : {}", key.getUserId(), key.getUserType(), e);
127 - }
128 - }
129 - userSessions.put(key, new UserSessionData(session));
130 - log.info("WebSocket connect success, userId : {}, userType : {}", key.getUserId(), key.getUserType());
131 } else { 135 } else {
132 - log.warn("WebSocket connect failed,unable to get valid user information"); 136 + log.warn("WebSocket connection failed; unable to retrieve valid user information");
133 } 137 }
134 } 138 }
135 139
140 +
136 @Override 141 @Override
137 public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { 142 public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
138 final var key = getUserSessionKey(session); 143 final var key = getUserSessionKey(session);
139 if (key != null) { 144 if (key != null) {
140 - userSessions.remove(key); 145 + userSessions.computeIfPresent(key, (k, sessionList) -> {
141 - log.info("WebSocket closed: userId : {}, userType : {}", key.getUserId(), key.getUserType()); 146 + sessionList.removeIf(userData -> userData.getSession().getId().equals(session.getId()));
147 + return sessionList.isEmpty() ? null : sessionList;
148 + });
149 + log.info("WebSocket closed; userId: {}, userType: {}", key.getUserId(), key.getUserType());
142 } else { 150 } else {
143 - log.warn("WebSocket closed failed,unable to get valid user information"); 151 + log.warn("WebSocket closed; unable to retrieve valid user information");
144 } 152 }
145 } 153 }
146 } 154 }
...\ No newline at end of file ...\ No newline at end of file
......