|
@@ -0,0 +1,156 @@
|
|
|
+package com.alvin;
|
|
|
+
|
|
|
+import com.alvin.dto.WebsocketMsgDTO;
|
|
|
+import com.alvin.util.JsonUtil;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.logging.log4j.util.Strings;
|
|
|
+import org.json.JSONObject;
|
|
|
+import org.springframework.stereotype.Component;
|
|
|
+
|
|
|
+import javax.websocket.OnClose;
|
|
|
+import javax.websocket.OnMessage;
|
|
|
+import javax.websocket.OnOpen;
|
|
|
+import javax.websocket.Session;
|
|
|
+import javax.websocket.server.PathParam;
|
|
|
+import javax.websocket.server.ServerEndpoint;
|
|
|
+import java.io.IOException;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.time.format.DateTimeFormatter;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Objects;
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
+import java.util.concurrent.atomic.AtomicInteger;
|
|
|
+
|
|
|
+/**
|
|
|
+ * WebSocketServer服务
|
|
|
+ *
|
|
|
+ * @author AnYuan
|
|
|
+ */
|
|
|
+@ServerEndpoint(value = "/webSocket/{uid}")
|
|
|
+@Component
|
|
|
+@Slf4j
|
|
|
+public class WebSocketServer {
|
|
|
+ /**
|
|
|
+ * 机器⼈发⾔名称
|
|
|
+ */
|
|
|
+ private static final String SPOKESMAN_ADMIN = "机器⼈";
|
|
|
+ /**
|
|
|
+ * concurrent包的线程安全Set
|
|
|
+ * ⽤来存放每个客户端对应的Session对象
|
|
|
+ */
|
|
|
+ private static final ConcurrentHashMap<String, Session> SESSION_POOLS = new ConcurrentHashMap<>();
|
|
|
+ /**
|
|
|
+ * 静态变量,⽤来记录当前在线连接数。
|
|
|
+ * 应该把它设计成线程安全的。
|
|
|
+ */
|
|
|
+ private static final AtomicInteger ONLINE_NUM = new AtomicInteger();
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取在线⽤户列表
|
|
|
+ *
|
|
|
+ * @return List<String>
|
|
|
+ */
|
|
|
+ private List<String> getOnlineUsers() {
|
|
|
+ return new ArrayList<>(SESSION_POOLS.keySet());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * ⽤户建⽴连接成功调⽤
|
|
|
+ *
|
|
|
+ * @param session ⽤户集合
|
|
|
+ * @param uid ⽤户标志
|
|
|
+ */
|
|
|
+ @OnOpen
|
|
|
+ public void onOpen(Session session, @PathParam(value = "uid") String uid) {// 将加⼊连接的⽤户加⼊SESSION_POOLS集合
|
|
|
+ SESSION_POOLS.put(uid, session);
|
|
|
+ // 在线⽤户+1
|
|
|
+ ONLINE_NUM.incrementAndGet();
|
|
|
+ sendToAll(new WebsocketMsgDTO(SPOKESMAN_ADMIN, uid + " 加⼊连接!", getOnlineUsers()));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * ⽤户关闭连接时调⽤
|
|
|
+ *
|
|
|
+ * @param uid ⽤户标志
|
|
|
+ */
|
|
|
+ @OnClose
|
|
|
+ public void onClose(@PathParam(value = "uid") String uid) {
|
|
|
+ // 将加⼊连接的⽤户移除SESSION_POOLS集合
|
|
|
+ SESSION_POOLS.remove(uid);
|
|
|
+ // 在线⽤户-1
|
|
|
+ ONLINE_NUM.decrementAndGet();
|
|
|
+ sendToAll(new WebsocketMsgDTO(SPOKESMAN_ADMIN, uid + " 断开连接!", getOnlineUsers()));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 服务端收到客户端信息
|
|
|
+ *
|
|
|
+ * @param message 客户端发来的string
|
|
|
+ * @param uid uid ⽤户标志
|
|
|
+ */
|
|
|
+ @OnMessage
|
|
|
+ public void onMessage(String message, @PathParam(value = "uid") String uid) {
|
|
|
+ log.info("Client:[{}], Message: [{}]", uid, message);
|
|
|
+ // 接收并解析前端消息并加上时间,最后根据是否有接收⽤户,区别发送所有⽤户还是单个⽤户
|
|
|
+ WebsocketMsgDTO msgDTO = JsonUtil.getObject(message, WebsocketMsgDTO.class);
|
|
|
+ msgDTO.setDateTime(localDateTimeToString());
|
|
|
+ // 如果有接收⽤户就发送单个⽤户
|
|
|
+ if (Strings.isNotBlank(msgDTO.getToUId())) {
|
|
|
+ sendMsgByUid(msgDTO);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ // 否则发送所有⼈
|
|
|
+ sendToAll(msgDTO);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 给所有⼈发送消息
|
|
|
+ *
|
|
|
+ * @param msgDTO msgDTO
|
|
|
+ */
|
|
|
+ private void sendToAll(WebsocketMsgDTO msgDTO) {
|
|
|
+ //构建json消息体
|
|
|
+ String content = JsonUtil.toJsonStr(msgDTO);
|
|
|
+ // 遍历发送所有在线⽤户
|
|
|
+ SESSION_POOLS.forEach((k, session) -> sendMessage(session, content));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 给指定⽤户发送信息
|
|
|
+ */
|
|
|
+ private void sendMsgByUid(WebsocketMsgDTO msgDTO) {
|
|
|
+ sendMessage(SESSION_POOLS.get(msgDTO.getToUId()), JsonUtil.toJsonStr(msgDTO));
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 发送消息⽅法
|
|
|
+ *
|
|
|
+ * @param session ⽤户
|
|
|
+ * @param content 消息
|
|
|
+ */
|
|
|
+ private void sendMessage(Session session, String content) {
|
|
|
+ try {
|
|
|
+ if (Objects.nonNull(session)) {
|
|
|
+ // 使⽤Synchronized锁防⽌多次发送消息
|
|
|
+ synchronized (session) {
|
|
|
+ // 发送消息
|
|
|
+ session.getBasicRemote().sendText(content);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (IOException ioException) {
|
|
|
+ log.info("发送消息失败:{}", ioException.getMessage());
|
|
|
+ ioException.printStackTrace();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取当前时间
|
|
|
+ *
|
|
|
+ * @return String 12:00:00
|
|
|
+ */
|
|
|
+ private String localDateTimeToString() {
|
|
|
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
|
|
|
+ return dateTimeFormatter.format(LocalDateTime.now());
|
|
|
+ }
|
|
|
+}
|