tianyunperfect 3 years ago
parent
commit
dbcc59b3fa

+ 8 - 0
springboot-main/pom.xml

@@ -49,6 +49,14 @@
             <artifactId>commons-pool2</artifactId>
             <version>2.11.1</version>
         </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+        </dependency>
     </dependencies>
 
 </project>

+ 4 - 20
springboot-main/src/main/java/com/alvin/Application.java

@@ -1,33 +1,17 @@
 package com.alvin;
 
 
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
 import redis.clients.jedis.*;
 
 import java.util.Arrays;
 import java.util.HashSet;
 import java.util.Set;
 
+@SpringBootApplication
 public class Application {
     public static void main(String[] args) throws Exception {
-
-        Set<String> hosts = new HashSet<>();
-        hosts.add("127.0.0.1:26379");
-        //hosts.add("127.0.0.1:36379"); 配置多个哨兵
-
-        JedisSentinelPool pool = new JedisSentinelPool("mymaster", hosts);
-        pool.setMaxTotal(10);
-        Jedis jedis = null;
-
-        for (int i = 0; i < 20; i++) {
-            Thread.sleep(2000);
-            try {
-                jedis = pool.getResource();
-                String v = "hi" + i;
-                jedis.set("hello", v);
-                System.out.println(v + "-->" + jedis.get("hello").equals(v));
-            } catch (Exception e) {
-                System.out.println(" [ exception happened]" + e);
-            }
-        }
+        SpringApplication.run(Application.class);
     }
 }

+ 156 - 0
springboot-main/src/main/java/com/alvin/WebSocketServer.java

@@ -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());
+    }
+}

+ 13 - 0
springboot-main/src/main/java/com/alvin/config/WebsocketConfig.java

@@ -0,0 +1,13 @@
+package com.alvin.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+@Configuration
+public class WebsocketConfig {
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter() {
+        return new ServerEndpointExporter();
+    }
+}

+ 55 - 0
springboot-main/src/main/java/com/alvin/dto/WebsocketMsgDTO.java

@@ -0,0 +1,55 @@
+package com.alvin.dto;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.List;
+
+@Data
+public class WebsocketMsgDTO {
+    /**
+     * 发送消息⽤户
+     */
+    private String uid;
+    /**
+     * 接收消息⽤户
+     */
+    private String toUId;
+    /**
+     * 消息内容
+     */
+    private String content;
+    /**
+     * 消息时间
+     */
+    private String dateTime;
+    /**
+     * ⽤户列表
+     */
+    private List<String> onlineUser;
+
+    /**
+     * 统⼀消息模版
+     *
+     * @param uid        发送消息⽤户
+     * @param content    消息内容
+     * @param onlineUser 在线⽤户列表
+     */
+    public WebsocketMsgDTO(String uid, String content, List<String> onlineUser) {
+        this.uid = uid;
+        this.content = content;
+        this.onlineUser = onlineUser;
+        this.dateTime = localDateTimeToString();
+    }
+
+    /**
+     * 获取当前时间
+     *
+     * @return String 12:00:00
+     */
+    private String localDateTimeToString() {
+        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
+        return dateTimeFormatter.format(LocalDateTime.now());
+    }
+}

+ 75 - 0
springboot-main/src/main/resources/static/admin.html

@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+<head>
+  <title>Admin Hello WebSocket</title>
+  <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
+  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.2.1/jquery.js"></script>
+  <script src="app.js"></script>
+  <style>
+    body {
+      background-color: #f5f5f5;
+    }
+    #main-content {
+      max-width: 940px;
+      padding: 2em 3em;
+      margin: 0 auto 20px;
+      background-color: #fff;
+      border: 1px solid #e5e5e5;
+      -webkit-border-radius: 5px;
+      -moz-border-radius: 5px;
+      border-radius: 5px;
+    }
+  </style>
+</head>
+<body>
+<div id="main-content" class="container">
+  <div class="row">
+    <div class="col-md-6">
+      <form class="form-inline">
+        <div class="form-group">
+          <input id="userId" value="Admin" hidden>
+          <label for="connect">建⽴连接通道:</label>
+          <button id="connect" class="btn btn-default" type="submit">Connect</button>
+          <button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
+          </button>
+        </div>
+      </form>
+    </div>
+    <div class="col-md-6">
+      <form class="form-inline">
+        <div class="form-group">
+          <label>发布新公告</label>
+          <input type="text" id="content" class="form-control" value="" placeholder="发⾔框..">
+        </div>
+        <button id="send" class="btn btn-default" type="submit">发布</button>
+      </form>
+    </div>
+  </div>
+  <div class="row" style="margin-top: 30px">
+    <div class="col-md-12">
+      <table id="userlist" class="table table-striped">
+        <thead>
+        <tr>
+          <th>实时在线⽤户列表<span id="onLineUserCount"></span></th>
+        </tr>
+        </thead>
+        <tbody id='online'>
+        </tbody>
+      </table>
+    </div>
+    <div class="col-md-12">
+      <table id="conversation" class="table table-striped">
+        <thead>
+        <tr>
+          <th>游戏公告内容</th>
+        </tr>
+        </thead>
+        <tbody id="notice">
+        </tbody>
+      </table>
+    </div>
+  </div>
+</div>
+</body>
+</html>

+ 108 - 0
springboot-main/src/main/resources/static/app.js

@@ -0,0 +1,108 @@
+let socket;
+
+function setConnected(connected) {
+    $("#connect").prop("disabled", connected);
+    $("#disconnect").prop("disabled", !connected);
+    if (connected) {
+        $("#conversation").show();
+    } else {
+        $("#conversation").hide();
+    }
+    $("#notice").html("");
+}
+
+// WebSocket 服务操作
+function openSocket() {
+    if (typeof (WebSocket) == "undefined") {
+        console.log("浏览器不⽀持WebSocket");
+    } else {
+        console.log("浏览器⽀持WebSocket");
+        //实现化WebSocket对象,指定要连接的服务器地址与端⼝建⽴连接
+        if (socket != null) {
+            socket.close();
+            socket = null;
+        }
+        // ws 为websocket连接标识,localhost:9999 为SpringBoot的连接地址,webSocket 为后端配置的前缀, userId 则是参数
+        socket = new WebSocket("ws://localhost:8080/webSocket/" + $("#userId").val());
+        //打开事件
+        socket.onopen = function () {
+            console.log("websocket已打开");
+            setConnected(true)
+        };
+        //获得消息事件
+        socket.onmessage = function (msg) {
+            const msgDto = JSON.parse(msg.data);
+            console.log(msg)
+            showContent(msgDto);
+            showOnlineUser(msgDto.onlineUser);
+        };
+        //关闭事件
+        socket.onclose = function () {
+            console.log("websocket已关闭");
+            setConnected(false)
+            removeOnlineUser();
+        };
+        //发⽣了错误事件
+        socket.onerror = function () {
+            setConnected(false)
+            console.log("websocket发⽣了错误");
+        }
+    }
+}
+
+//2、关闭连接
+function disconnect() {
+    if (socket !== null) {
+        socket.close();
+    }
+    setConnected(false);
+    console.log("Disconnected");
+}
+
+function sendMessage() {
+    if (typeof (WebSocket) == "undefined") {
+        console.log("您的浏览器不⽀持WebSocket");
+    } else {
+        var msg = '{"uid":"' + $("#userId").val() + '", "toUId": null, "content":"' + $("#content").val() + '"}';
+        console.log("向服务端发送消息体:" + msg);
+        socket.send(msg);
+    }
+}
+
+// 订阅的消息显⽰在客户端指定位置
+function showContent(serverMsg) {
+    $("#notice").html("<tr><td>" + serverMsg.uid + ": </td> <td>" + serverMsg.content + "</td><td>" + serverMsg.dateTime + "</td></tr>" + $("#notice").html())
+}
+
+//显⽰实时在线⽤户
+function showOnlineUser(serverMsg) {
+    if (null != serverMsg) {
+        let html = '';
+        for (let i = 0; i < serverMsg.length; i++) {
+            html += "<tr><td>" + serverMsg[i] + "</td></tr>";
+        }
+        $("#online").html(html);
+        $("#onLineUserCount").html(" ( " + serverMsg.length + " )");
+    }
+}
+
+//显⽰实时在线⽤户
+function removeOnlineUser() {
+    $("#online").html("");
+    $("#onLineUserCount").html("");
+}
+
+$(function () {
+    $("form").on('submit', function (e) {
+        e.preventDefault();
+    });
+    $("#connect").click(function () {
+        openSocket();
+    });
+    $("#disconnect").click(function () {
+        disconnect();
+    });
+    $("#send").click(function () {
+        sendMessage();
+    });
+});

+ 75 - 0
springboot-main/src/main/resources/static/user.html

@@ -0,0 +1,75 @@
+<!DOCTYPE html>
+<html>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+<head>
+  <title>Admin Hello WebSocket</title>
+  <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
+  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.2.1/jquery.js"></script>
+  <script src="app.js"></script>
+  <style>
+    body {
+      background-color: #f5f5f5;
+    }
+    #main-content {
+      max-width: 940px;
+      padding: 2em 3em;
+      margin: 0 auto 20px;
+      background-color: #fff;
+      border: 1px solid #e5e5e5;
+      -webkit-border-radius: 5px;
+      -moz-border-radius: 5px;
+      border-radius: 5px;
+    }
+  </style>
+</head>
+<body>
+<div id="main-content" class="container">
+  <div class="row">
+    <div class="col-md-6">
+      <form class="form-inline">
+        <div class="form-group">
+          <input id="userId" value="user1" hidden>
+          <label for="connect">建⽴连接通道:</label>
+          <button id="connect" class="btn btn-default" type="submit">Connect</button>
+          <button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
+          </button>
+        </div>
+      </form>
+    </div>
+<!--    <div class="col-md-6">-->
+<!--      <form class="form-inline">-->
+<!--        <div class="form-group">-->
+<!--          <label>发布新公告</label>-->
+<!--          <input type="text" id="content" class="form-control" value="" placeholder="发⾔框..">-->
+<!--        </div>-->
+<!--        <button id="send" class="btn btn-default" type="submit">发布</button>-->
+<!--      </form>-->
+<!--    </div>-->
+  </div>
+  <div class="row" style="margin-top: 30px">
+    <div class="col-md-12">
+      <table id="userlist" class="table table-striped">
+        <thead>
+        <tr>
+          <th>实时在线⽤户列表<span id="onLineUserCount"></span></th>
+        </tr>
+        </thead>
+        <tbody id='online'>
+        </tbody>
+      </table>
+    </div>
+    <div class="col-md-12">
+      <table id="conversation" class="table table-striped">
+        <thead>
+        <tr>
+          <th>游戏公告内容</th>
+        </tr>
+        </thead>
+        <tbody id="notice">
+        </tbody>
+      </table>
+    </div>
+  </div>
+</div>
+</body>
+</html>