• WebSocket+Java 私聊、群聊实例


       前言

      之前写毕业设计的时候就想加上聊天系统,当时已经用ajax长轮询实现了一个(还不懂什么是轮询机制的,猛戳这里:https://www.cnblogs.com/hoojo/p/longPolling_comet_jquery_iframe_ajax.html),但由于种种原因没有加到毕设里面。后来回校答辩后研究了一下websocket,并参照网上资料写了一个简单的聊天,现在又重新整理并记录下来。

      以下介绍来自维基百科:
      WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket通信协议于2011年被IETF定为标准RFC 6455,并由RFC7936补充规范。WebSocket API也被W3C定为标准。
    WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

      这里可以看一下官网介绍:http://www.websocket.org/aboutwebsocket.html
      官网里面介绍非常详细,我就不做搬运工了,要是有像我一样英语不好的同学,右键->翻译成简体中文

      spring对websocket的支持:https://docs.spring.io/spring/docs/4.3.13.RELEASE/spring-framework-reference/htmlsingle/#websocket
      这里有一份spring对websocket的详细介绍:https://docs.spring.io/spring/docs/5.1.8.RELEASE/spring-framework-reference/web.html#websocket

      四个大章节,内容很多,就不一一展开介绍了

      

      效果

      2019/04/30补充:我们这个登录、登出非常简单,就一个请求地址,连页面都没有,所以刚开始我就没有贴出来,导致博客文章阅读起来比较吃力,现在在这里补充一下(由于项目后面有所改动,请求地址少了 springboot/,不过大家看得懂就行),这里只是一个小demo,所有就怎么简单怎么来

      登录 http://localhost:10086/websocket/login/huanzi,

      登出 http://localhost:10086/websocket/logout/huanzi

        

      上下线有提示

      如果这时候发送消息给离线的人,则会收到系统提示消息

      群聊

      本例中,点击自己是群聊窗口

      huanzi一发送群聊,laowang跟xiaofang都不是在当前群聊窗口,出现小圆点+1

      

      huanzi一发送群聊,xiaofang在当前群聊窗口,直接追加消息,老王不在对应的聊天窗口,出现小圆点+1

      xiaofang回复,huanzi直接追加消息,laowang依旧小圆点+1

      laowang点击群聊窗口,小圆点消失,追加群聊消息

      laowang参与群聊

      xiaofang切出群聊窗口,laowang在群聊发送消息,xiaofang出现小圆点+1

      切回来,小圆点消失,聊天数据正常接收追加

      三方正常参与聊天

      私聊

      huanzis私聊xiaofang,xiaofang聊天窗口在群聊,小圆点+1,而laowang不受影响

       xiaofang切到私聊窗口,小圆点消失,数据正常追加;huanzi刚好处于私聊窗口,数据直接追加

       效果演示到此结束,下面贴出代码

      代码编写

      首先先介绍一下项目结构

      maven

            <!-- springboot websocket -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
            </dependency>
    
            <!-- thymeleaf模板 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>

      配置文件

    #修改thymeleaf访问根路径
    spring.thymeleaf.prefix=classpath:/view/
    socketChart.css样式
    body{
        background-color: #efebdc;
    }
    #hz-main{
        width: 700px;
        height: 500px;
        background-color: red;
        margin: 0 auto;
    }
    
    #hz-message{
        width: 500px;
        height: 500px;
        float: left;
        background-color: #B5B5B5;
    }
    
    #hz-message-body{
        width: 460px;
        height: 340px;
        background-color: #E0C4DA;
        padding: 10px 20px;
        overflow:auto;
    }
    
    #hz-message-input{
        width: 500px;
        height: 99px;
        background-color: white;
        overflow:auto;
    }
    
    #hz-group{
        width: 200px;
        height: 500px;
        background-color: rosybrown;
        float: right;
    }
    
    .hz-message-list{
        min-height: 30px;
        margin: 10px 0;
    }
    .hz-message-list-text{
        padding: 7px 13px;
        border-radius: 15px;
        width: auto;
        max-width: 85%;
        display: inline-block;
    }
    
    .hz-message-list-username{
        margin: 0;
    }
    
    .hz-group-body{
        overflow:auto;
    }
    
    .hz-group-list{
        padding: 10px;
    }
    
    .left{
        float: left;
        color: #595a5a;
        background-color: #ebebeb;
    }
    .right{
        float: right;
        color: #f7f8f8;
        background-color: #919292;
    }
    .hz-badge{
        width: 20px;
        height: 20px;
        background-color: #FF5722;
        border-radius: 50%;
        float: right;
        color: white;
        text-align: center;
        line-height: 20px;
        font-weight: bold;
        opacity: 0;
    }

      

    socketChart.html页面
    <!DOCTYPE>
    <!--解决idea thymeleaf 表达式模板报红波浪线-->
    <!--suppress ALL -->
    <html xmlns:th="http://www.thymeleaf.org">
    <head>
        <title>聊天页面</title>
        <!-- jquery在线版本 -->
        <script src="http://libs.baidu.com/jquery/2.1.4/jquery.min.js"></script>
        <!--引入样式-->
        <link th:href="@{/css/socketChart.css}" rel="stylesheet" type="text/css"/>
    </head>
    <body>
    <div id="hz-main">
        <div id="hz-message">
            <!-- 头部 -->
            正在与<span id="toUserName"></span>聊天
            <hr style="margin: 0px;"/>
            <!-- 主体 -->
            <div id="hz-message-body">
            </div>
            <!-- 功能条 -->
            <div id="">
                <button>表情</button>
                <button>图片</button>
                <button id="videoBut">视频</button>
                <button onclick="send()" style="float: right;">发送</button>
            </div>
            <!-- 输入框 -->
            <div contenteditable="true" id="hz-message-input">
    
            </div>
        </div>
        <div id="hz-group">
            登录用户:<span id="talks" th:text="${username}">请登录</span>
            <br/>
            在线人数:<span id="onlineCount">0</span>
            <!-- 主体 -->
            <div id="hz-group-body">
    
            </div>
        </div>
    </div>
    </body>
    <script type="text/javascript" th:inline="javascript">
        //项目根路径
        var ctx = [[${#request.getContextPath()}]];//登录名
        var username = /*[[${username}]]*/'';
    </script>
    <script th:src="@{/js/socketChart.js}"></script>
    </html>

      

    socketChart.js 逻辑代码
        //消息对象数组
        var msgObjArr = new Array();
    
        var websocket = null;
    
        //判断当前浏览器是否支持WebSocket, springboot是项目名
        if ('WebSocket' in window) {
            websocket = new WebSocket("ws://localhost:10086/springboot/websocket/"+username);
        } else {
            console.error("不支持WebSocket");
        }
    
        //连接发生错误的回调方法
        websocket.onerror = function (e) {
            console.error("WebSocket连接发生错误");
        };
    
        //连接成功建立的回调方法
        websocket.onopen = function () {
            //获取所有在线用户
            $.ajax({
                type: 'post',
                url: ctx + "/websocket/getOnlineList",
                contentType: 'application/json;charset=utf-8',
                dataType: 'json',
                data: {username:username},
                success: function (data) {
                    if (data.length) {
                        //列表
                        for (var i = 0; i < data.length; i++) {
                            var userName = data[i];
                            $("#hz-group-body").append("<div class="hz-group-list"><span class='hz-group-list-username'>" + userName + "</span><span id="" + userName + "-status">[在线]</span><div id="hz-badge-" + userName + "" class='hz-badge'>0</div></div>");
                        }
    
                        //在线人数
                        $("#onlineCount").text(data.length);
                    }
                },
                error: function (xhr, status, error) {
                    console.log("ajax错误!");
                }
            });
        }
    
        //接收到消息的回调方法
        websocket.onmessage = function (event) {
            var messageJson = eval("(" + event.data + ")");
    
            //普通消息(私聊)
            if (messageJson.type == "1") {
                //来源用户
                var srcUser = messageJson.srcUser;
                //目标用户
                var tarUser = messageJson.tarUser;
                //消息
                var message = messageJson.message;
    
                //最加聊天数据
                setMessageInnerHTML(srcUser.username,srcUser.username, message);
            }
    
            //普通消息(群聊)
            if (messageJson.type == "2"){
                //来源用户
                var srcUser = messageJson.srcUser;
                //目标用户
                var tarUser = messageJson.tarUser;
                //消息
                var message = messageJson.message;
    
                //最加聊天数据
                setMessageInnerHTML(username,tarUser.username, message);
            }
    
            //对方不在线
            if (messageJson.type == "0"){
                //消息
                var message = messageJson.message;
    
                $("#hz-message-body").append(
                    "<div class="hz-message-list" style='text-align: center;'>" +
                        "<div class="hz-message-list-text">" +
                            "<span>" + message + "</span>" +
                        "</div>" +
                    "</div>");
            }
    
            //在线人数
            if (messageJson.type == "onlineCount") {
                //取出username
                var onlineCount = messageJson.onlineCount;
                var userName = messageJson.username;
                var oldOnlineCount = $("#onlineCount").text();
    
                //新旧在线人数对比
                if (oldOnlineCount < onlineCount) {
                    if($("#" + userName + "-status").length > 0){
                        $("#" + userName + "-status").text("[在线]");
                    }else{
                        $("#hz-group-body").append("<div class="hz-group-list"><span class='hz-group-list-username'>" + userName + "</span><span id="" + userName + "-status">[在线]</span><div id="hz-badge-" + userName + "" class='hz-badge'>0</div></div>");
                    }
                } else {
                    //有人下线
                    $("#" + userName + "-status").text("[离线]");
                }
                $("#onlineCount").text(onlineCount);
            }
    
        }
    
        //连接关闭的回调方法
        websocket.onclose = function () {
            //alert("WebSocket连接关闭");
        }
    
        //将消息显示在对应聊天窗口    对于接收消息来说这里的toUserName就是来源用户,对于发送来说则相反
        function setMessageInnerHTML(srcUserName,msgUserName, message) {
            //判断
            var childrens = $("#hz-group-body").children(".hz-group-list");
            var isExist = false;
            for (var i = 0; i < childrens.length; i++) {
                var text = $(childrens[i]).find(".hz-group-list-username").text();
                if (text == srcUserName) {
                    isExist = true;
                    break;
                }
            }
            if (!isExist) {
                //追加聊天对象
                msgObjArr.push({
                    toUserName: srcUserName,
                    message: [{username: msgUserName, message: message, date: NowTime()}]//封装数据
                });
                $("#hz-group-body").append("<div class="hz-group-list"><span class='hz-group-list-username'>" + srcUserName + "</span><span id="" + srcUserName + "-status">[在线]</span><div id="hz-badge-" + srcUserName + "" class='hz-badge'>0</div></div>");
            } else {
                //取出对象
                var isExist = false;
                for (var i = 0; i < msgObjArr.length; i++) {
                    var obj = msgObjArr[i];
                    if (obj.toUserName == srcUserName) {
                        //保存最新数据
                        obj.message.push({username: msgUserName, message: message, date: NowTime()});
                        isExist = true;
                        break;
                    }
                }
                if (!isExist) {
                    //追加聊天对象
                    msgObjArr.push({
                        toUserName: srcUserName,
                        message: [{username: msgUserName, message: message, date: NowTime()}]//封装数据
                    });
                }
            }
    
            // 对于接收消息来说这里的toUserName就是来源用户,对于发送来说则相反
            var username = $("#toUserName").text();
    
            //刚好打开的是对应的聊天页面
            if (srcUserName == username) {
                $("#hz-message-body").append(
                    "<div class="hz-message-list">" +
                        "<p class='hz-message-list-username'>"+msgUserName+":</p>" +
                    "<div class="hz-message-list-text left">" +
                        "<span>" + message + "</span>" +
                    "</div>" +
                    "<div style=" clear: both; "></div>" +
                    "</div>");
            } else {
                //小圆点++
                var conut = $("#hz-badge-" + srcUserName).text();
                $("#hz-badge-" + srcUserName).text(parseInt(conut) + 1);
                $("#hz-badge-" + srcUserName).css("opacity", "1");
            }
        }
    
        //发送消息
        function send() {
            //消息
            var message = $("#hz-message-input").html();
            //目标用户名
            var tarUserName = $("#toUserName").text();
            //登录用户名
            var srcUserName = $("#talks").text();
            websocket.send(JSON.stringify({
                "type": "1",
                "tarUser": {"username": tarUserName},
                "srcUser": {"username": srcUserName},
                "message": message
            }));
            $("#hz-message-body").append(
                "<div class="hz-message-list">" +
                "<div class="hz-message-list-text right">" +
                "<span>" + message + "</span>" +
                "</div>" +
                "</div>");
            $("#hz-message-input").html("");
            //取出对象
            if (msgObjArr.length > 0) {
                var isExist = false;
                for (var i = 0; i < msgObjArr.length; i++) {
                    var obj = msgObjArr[i];
                    if (obj.toUserName == tarUserName) {
                        //保存最新数据
                        obj.message.push({username: srcUserName, message: message, date: NowTime()});
                        isExist = true;
                        break;
                    }
                }
                if (!isExist) {
                    //追加聊天对象
                    msgObjArr.push({
                        toUserName: tarUserName,
                        message: [{username: srcUserName, message: message, date: NowTime()}]//封装数据[{username:huanzi,message:"你好,我是欢子!",date:2018-04-29 22:48:00}]
                    });
                }
            } else {
                //追加聊天对象
                msgObjArr.push({
                    toUserName: tarUserName,
                    message: [{username: srcUserName, message: message, date: NowTime()}]//封装数据[{username:huanzi,message:"你好,我是欢子!",date:2018-04-29 22:48:00}]
                });
            }
        }
    
        //监听点击用户
        $("body").on("click", ".hz-group-list", function () {
            $(".hz-group-list").css("background-color", "");
            $(this).css("background-color", "whitesmoke");
            $("#toUserName").text($(this).find(".hz-group-list-username").text());
    
            //清空旧数据,从对象中取出并追加
            $("#hz-message-body").empty();
            $("#hz-badge-" + $("#toUserName").text()).text("0");
            $("#hz-badge-" + $("#toUserName").text()).css("opacity", "0");
            if (msgObjArr.length > 0) {
                for (var i = 0; i < msgObjArr.length; i++) {
                    var obj = msgObjArr[i];
                    if (obj.toUserName == $("#toUserName").text()) {
                        //追加数据
                        var messageArr = obj.message;
                        if (messageArr.length > 0) {
                            for (var j = 0; j < messageArr.length; j++) {
                                var msgObj = messageArr[j];
                                var leftOrRight = "right";
                                var message = msgObj.message;
                                var msgUserName = msgObj.username;
                                var toUserName = $("#toUserName").text();
    
                                //当聊天窗口与msgUserName的人相同,文字在左边(对方/其他人),否则在右边(自己)
                                if (msgUserName == toUserName) {
                                    leftOrRight = "left";
                                }
    
                                //但是如果点击的是自己,群聊的逻辑就不太一样了
                                if (username == toUserName && msgUserName != toUserName) {
                                    leftOrRight = "left";
                                }
    
                                if (username == toUserName && msgUserName == toUserName) {
                                    leftOrRight = "right";
                                }
    
                                var magUserName = leftOrRight == "left" ? "<p class='hz-message-list-username'>"+msgUserName+":</p>" : "";
    
                                $("#hz-message-body").append(
                                    "<div class="hz-message-list">" +
                                    magUserName+
                                    "<div class="hz-message-list-text " + leftOrRight + "">" +
                                        "<span>" + message + "</span>" +
                                    "</div>" +
                                    "<div style=" clear: both; "></div>" +
                                    "</div>");
                            }
                        }
                        break;
                    }
                }
            }
        });
    
        //获取当前时间
        function NowTime() {
            var time = new Date();
            var year = time.getFullYear();//获取年
            var month = time.getMonth() + 1;//或者月
            var day = time.getDate();//或者天
            var hour = time.getHours();//获取小时
            var minu = time.getMinutes();//获取分钟
            var second = time.getSeconds();//或者秒
            var data = year + "-";
            if (month < 10) {
                data += "0";
            }
            data += month + "-";
            if (day < 10) {
                data += "0"
            }
            data += day + " ";
            if (hour < 10) {
                data += "0"
            }
            data += hour + ":";
            if (minu < 10) {
                data += "0"
            }
            data += minu + ":";
            if (second < 10) {
                data += "0"
            }
            data += second;
            return data;
        }

      java代码有三个类,MyEndpointConfigure,WebSocketConfig,WebSocketServer;

      MyEndpointConfigure

    /**
     * 解决注入其他类的问题,详情参考这篇帖子:webSocket无法注入其他类:https://blog.csdn.net/tornadojava/article/details/78781474
     */
    public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware {
    
        private static volatile BeanFactory context;
    
        @Override
        public <T> T getEndpointInstance(Class<T> clazz){
            return context.getBean(clazz);
        }
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            MyEndpointConfigure.context = applicationContext;
        }
    }

      WebSocketConfig

    /**
     * WebSocket配置
     */
    @Configuration
    public class WebSocketConfig{
    
    
        /**
         * 用途:扫描并注册所有携带@ServerEndpoint注解的实例。 @ServerEndpoint("/websocket")
         * PS:如果使用外部容器 则无需提供ServerEndpointExporter。
         */
        @Bean
        public ServerEndpointExporter serverEndpointExporter() {
            return new ServerEndpointExporter();
        }
    
        /**
         * 支持注入其他类
         */
        @Bean
        public MyEndpointConfigure  newMyEndpointConfigure (){
            return new MyEndpointConfigure ();
        }
    }

      WebSocketServer

    /**
     * WebSocket服务
     */
    @RestController
    @RequestMapping("/websocket")
    @ServerEndpoint(value = "/websocket/{username}", configurator = MyEndpointConfigure.class)
    public class WebSocketServer {
    
        /**
         * 在线人数
         */
        private static int onlineCount = 0;
    
        /**
         * 在线用户的Map集合,key:用户名,value:Session对象
         */
        private static Map<String, Session> sessionMap = new HashMap<String, Session>();
    
        /**
         * 注入其他类(换成自己想注入的对象)
         */
        @Autowired
        private UserService userService;
    
        /**
         * 连接建立成功调用的方法
         */
        @OnOpen
        public void onOpen(Session session, @PathParam("username") String username) {
            //在webSocketMap新增上线用户
            sessionMap.put(username, session);
    
            //在线人数加加
            WebSocketServer.onlineCount++;
    
            //通知除了自己之外的所有人
            sendOnlineCount(session, "{'type':'onlineCount','onlineCount':" + WebSocketServer.onlineCount + ",username:'" + username + "'}");
        }
    
        /**
         * 连接关闭调用的方法
         */
        @OnClose
        public void onClose(Session session) {
            //下线用户名
            String logoutUserName = "";
    
            //从webSocketMap删除下线用户
            for (Entry<String, Session> entry : sessionMap.entrySet()) {
                if (entry.getValue() == session) {
                    sessionMap.remove(entry.getKey());
                    logoutUserName = entry.getKey();
                    break;
                }
            }
            //在线人数减减
            WebSocketServer.onlineCount--;
    
            //通知除了自己之外的所有人
            sendOnlineCount(session, "{'type':'onlineCount','onlineCount':" + WebSocketServer.onlineCount + ",username:'" + logoutUserName + "'}");
        }
    
        /**
         * 服务器接收到客户端消息时调用的方法
         */
        @OnMessage
        public void onMessage(String message, Session session) {
            try {
                //JSON字符串转 HashMap
                HashMap hashMap = new ObjectMapper().readValue(message, HashMap.class);
    
                //消息类型
                String type = (String) hashMap.get("type");
    
                //来源用户
                Map srcUser = (Map) hashMap.get("srcUser");
    
                //目标用户
                Map tarUser = (Map) hashMap.get("tarUser");
    
                //如果点击的是自己,那就是群聊
                if (srcUser.get("username").equals(tarUser.get("username"))) {
                    //群聊
                    groupChat(session,hashMap);
                } else {
                    //私聊
                    privateChat(session, tarUser, hashMap);
                }
    
                //后期要做消息持久化
    
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        /**
         * 发生错误时调用
         */
        @OnError
        public void onError(Session session, Throwable error) {
            error.printStackTrace();
        }
    
        /**
         * 通知除了自己之外的所有人
         */
        private void sendOnlineCount(Session session, String message) {
            for (Entry<String, Session> entry : sessionMap.entrySet()) {
                try {
                    if (entry.getValue() != session) {
                        entry.getValue().getBasicRemote().sendText(message);
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    
        /**
         * 私聊
         */
        private void privateChat(Session session, Map tarUser, HashMap hashMap) throws IOException {
            //获取目标用户的session
            Session tarUserSession = sessionMap.get(tarUser.get("username"));
    
            //如果不在线则发送“对方不在线”回来源用户
            if (tarUserSession == null) {
                session.getBasicRemote().sendText("{"type":"0","message":"对方不在线"}");
            } else {
                hashMap.put("type", "1");
                tarUserSession.getBasicRemote().sendText(new ObjectMapper().writeValueAsString(hashMap));
            }
        }
    
        /**
         * 群聊
         */
        private void groupChat(Session session,HashMap hashMap) throws IOException {
            for (Entry<String, Session> entry : sessionMap.entrySet()) {
                //自己就不用再发送消息了
                if (entry.getValue() != session) {
                    hashMap.put("type", "2");
                    entry.getValue().getBasicRemote().sendText(new ObjectMapper().writeValueAsString(hashMap));
                }
            }
        }
    
        /**
         * 登录
         */
        @RequestMapping("/login/{username}")
        public ModelAndView login(HttpServletRequest request, @PathVariable String username) {
            return new ModelAndView("socketChart.html", "username", username);
        }
    
        /**
         * 登出
         */
        @RequestMapping("/logout/{username}")
        public String loginOut(HttpServletRequest request, @PathVariable String username) {
            return "退出成功!";
        }
    
        /**
         * 获取在线用户
         */
        @RequestMapping("/getOnlineList")
        private List<String> getOnlineList(String username) {
            List<String> list = new ArrayList<String>();
            //遍历webSocketMap
            for (Entry<String, Session> entry : WebSocketServer.sessionMap.entrySet()) {
                if (!entry.getKey().equals(username)) {
                    list.add(entry.getKey());
                }
            }
            return list;
        }
    
    }

      后记

      后期把所有功能都补全就完美了,表情、图片都算比较简单,之前用轮询实现的时候写过了,但是没加到这里来;音视频聊天的话可以用WbeRTC来做,之前也研究了一下,不过还没搞完,这里贴一下维基百科对它的介绍,想了解更多的自行Google:

      WebRTC,名称源自网页即时通信(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的API。它于2011年6月1日开源并在Google、Mozilla、Opera支持下被纳入万维网联盟的W3C推荐标准。

      最后在加上持久化存储,注册后才能聊天,离线消息上线后接收,再加上用Redis或者其他的缓存技术支持,完美。不过聊天记录要做存储,表设计不知如何设计才合理,如果哪位大佬愿意分享可以留言给我,大家一起进步!

      补充

      2019-07-03补充:这里补充贴出pom代码,在子类引入父类,如果我们没有父类,只有一个子类,把两个整合一下就可以了

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>cn.huanzi.qch</groupId>
        <artifactId>parent</artifactId>
        <version>1.0.0</version>
        <packaging>pom</packaging>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.1.0.RELEASE</version>
            <relativePath/>
        </parent>
    
        <description>SpringBoot系列demo代码</description>
    
    
        <!-- 在父类引入一下通用的依赖 -->
        <dependencies>
            <!-- spring-boot-starter -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <!-- springboot web(MVC)-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <!-- springboot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <!--lombok插件 -->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
            </dependency>
    
            <!--热部署工具dev-tools-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <optional>true</optional>
                <scope>runtime</scope>
            </dependency>
        </dependencies>
    
        <!--构建工具-->
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <finalName>${project.artifactId}</finalName>
                        <outputDirectory>../package</outputDirectory>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    </project>
    parent.xml
    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <artifactId>springboot-websocket</artifactId>
        <version>0.0.1</version>
        <name>springboot-websocket</name>
        <description>SpringBoot系列——WebSocket</description>
    
        <!--继承父类-->
        <parent>
            <groupId>cn.huanzi.qch</groupId>
            <artifactId>parent</artifactId>
            <version>1.0.0</version>
        </parent>
    
        <dependencies>
            <!-- springboot websocket -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-websocket</artifactId>
            </dependency>
    
            <!-- thymeleaf模板 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    </project>
    springboot-websocket.xml

      在后记的部分我们就提到要加上持久化存储,事实上我们已经开始慢慢在写一套简单的IM即时通讯,已经实现到第三版了,持续更新中...

       一套简单的web即时通讯——第一版

      一套简单的web即时通讯——第二版

      一套简单的web即时通讯——第三版

      代码开源

      代码已经开源、托管到我的GitHub、码云:

      GitHub:https://github.com/huanzi-qch/springBoot

      码云:https://gitee.com/huanzi-qch/springBoot

  • 相关阅读:
    C/S与B/S应用的区别
    软件测试第三次作业-worldCount
    别再把你当成打工者,而是把你自己当成一个公司,来进行战略规划,逐步提升自己的价值
    未来,你可能不属于任何公司
    如何做职业转型的准备
    伯颜自留后路,项羽破釜沉舟。谁又是对的呢
    java基础之向上造型之后,调用方法的规则
    java入坑计划
    Python动态人脸识别
    Python人脸识别检测(本地图片)
  • 原文地址:https://www.cnblogs.com/huanzi-qch/p/9889521.html
Copyright © 2020-2023  润新知