Java+Springboot+Websocket online chat room

1. What is websocket?

Websocket is a WebSocket protocol defined by HTML5, which can better save server resources and bandwidth, and enable more real-time communication. It is a protocol for full-duplex communication over a single TCP connection. WebSocket makes the data exchange between the client and the server easier, allowing the server to actively push data to the client. In the WebSocket API, the browser and the server only need to complete a handshake, and a persistent connection can be created directly between the two, and two-way data transmission can be performed.

2. Why implement a web online chat program?

Recently, I saw the websocket protocol during the learning process, so I had an idea to use springboot+websocket to implement a demo of web online chat.

It took a little time to learn and build, and finally realized the following simple web online chat demo, and I will record it here. It is not only to share with everyone, but also to leave a trace of one's own learning, so as to continuously consolidate one's technical ability.

3. Realize the result

First upload a few screenshots to show the implementation results in advance
insert image description here

insert image description here
insert image description here

4. Implementation process

4.1. Create a springboot project named chat, and add the required maven dependencies in the pom.xml file

<dependencies>
		<!-- springboot依赖包 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <!-- json 工具包 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>fastjson</artifactId>
        <version>RELEASE</version>
    </dependency>
    <!-- websocket依赖包 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-websocket</artifactId>
    </dependency>
</dependencies>

4.2. Add yml configuration information

server:
  port: 8090
  servlet:
    context-path: /chatroom

spring:
  resources:
    static-locations: classpath:/,classpath:/static/

4.3. Modify the springboot startup class

package com.crwl.chatroom;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@SpringBootApplication
public class ChatroomApplication {
    
    

    public static void main(String[] args) {
    
    
        SpringApplication.run(ChatroomApplication.class, args);
    }
	//将ServerEndpointExporter 注册为一个spring的bean
    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
    
    
        return new ServerEndpointExporter();
    }
}

4.4. Prepare websocket tool class, dto class and enum class
WsTool.java (websocket tool class)

package com.crwl.chatroom.util;

import com.alibaba.fastjson.JSON;
import com.crwl.chatroom.dto.ChatMsg;
import com.crwl.chatroom.dto.ChatUser;
import com.crwl.chatroom.enums.ChatEnum;

import javax.websocket.RemoteEndpoint;
import javax.websocket.Session;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class WsTool {
    
    
    public static final Map<String, Session> LIVING_SESSIONS_CACHE = new ConcurrentHashMap<>();
    public static final Map<String, ChatUser>  LIVING_USER_CACHE = new ConcurrentHashMap<>();

    public static void sendMessageAll(ChatMsg chatMsg) {
    
    
        LIVING_SESSIONS_CACHE.forEach((sessionId, session) -> {
    
    
            sendMessage(session, chatMsg);
        });
    }
    /**
     * 发送给指定用户消息
     * @param session 用户 session
     * @param chatMsg 发送内容
     */
    public static void sendMessage(Session session, ChatMsg chatMsg) {
    
    
        if (session == null) {
    
    
            return;
        }
        final RemoteEndpoint.Basic basic = session.getBasicRemote();
        if (basic == null) {
    
    
            return;
        }
        try {
    
    
            basic.sendText(JSON.toJSONString(chatMsg));
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
    /***
     * 刷新在线用户
     */
    public static void refreshOnlineUserList(){
    
    
        ChatMsg chatMsg = new ChatMsg();
        chatMsg.setType(ChatEnum.USER_LIST.getCode());
        List<ChatUser> userList = new ArrayList<ChatUser>(WsTool.LIVING_USER_CACHE.values());
        chatMsg.setOnlineUserList(userList);
        sendMessageAll(chatMsg);
    }
}

ChatMsg.java (chat information Dto)

package com.crwl.chatroom.dto;

import java.util.List;

public class ChatMsg {
    
    
    //消息类型 1:聊天信息  2:刷新在线用户列表
    private String sendUserBh;
    private String sendUserXm;
    private String type;
    private String msg;
    private List<ChatUser> onlineUserList;

    public String getSendUserBh() {
    
    
        return sendUserBh;
    }

    public void setSendUserBh(String sendUserBh) {
    
    
        this.sendUserBh = sendUserBh;
    }

    public String getSendUserXm() {
    
    
        return sendUserXm;
    }

    public void setSendUserXm(String sendUserXm) {
    
    
        this.sendUserXm = sendUserXm;
    }

    public String getType() {
    
    
        return type;
    }

    public void setType(String type) {
    
    
        this.type = type;
    }

    public String getMsg() {
    
    
        return msg;
    }

    public void setMsg(String msg) {
    
    
        this.msg = msg;
    }

    public List<ChatUser> getOnlineUserList() {
    
    
        return onlineUserList;
    }

    public void setOnlineUserList(List<ChatUser> onlineUserList) {
    
    
        this.onlineUserList = onlineUserList;
    }
}

ChatUser.java (chat user information Dto)

package com.crwl.chatroom.dto;

public class ChatUser {
    
    
    private String userBh;
    private String userName;
    private String onlineTime;

    public String getUserBh() {
    
    
        return userBh;
    }

    public void setUserBh(String userBh) {
    
    
        this.userBh = userBh;
    }

    public String getUserName() {
    
    
        return userName;
    }

    public void setUserName(String userName) {
    
    
        this.userName = userName;
    }

    public String getOnlineTime() {
    
    
        return onlineTime;
    }

    public void setOnlineTime(String onlineTime) {
    
    
        this.onlineTime = onlineTime;
    }
}

Result.java (encapsulates the return object Dto of the Http request)

package com.crwl.chatroom.dto;


import java.io.Serializable;

/**
 * 返回的对象(统一返回)
 *
 * @author SmallStrong
 */
public class Result implements Serializable {
    
    
    private static final long serialVersionUID = 3337439376898084639L;

    /**
     * 处理状态 0成功,-1 失败
     */
    private Integer code;

    /**
     * 处理信息
     */
    private String msg;

    public Integer getCode() {
    
    
        return code;
    }

    public void setCode(Integer code) {
    
    
        this.code = code;
    }

    public String getMsg() {
    
    
        return msg;
    }

    public void setMsg(String msg) {
    
    
        this.msg = msg;
    }
}

ChatEnum.java (chat type enumeration class)

package com.crwl.chatroom.enums;

public enum ChatEnum {
    
    
    PUBLIC_MSG("1","公共聊天消息"),
    PRIVATE_MSG("2","私秘聊天信息"),
    CLOSE_SOCKET("3","关闭socket连接"),
    USER_LIST("4","在线用户列表"),
    JOIN_CHAT("5","加入聊天室");

    private String code;
    private String data;

    private ChatEnum(String code, String data) {
    
    
        this.code = code;
        this.data = data;
    }
    public String getCode() {
    
    
        return code;
    }
    public void setCode(String code) {
    
    
        this.code = code;
    }
    public String getData() {
    
    
        return data;
    }
    public void setData(String data) {
    
    
        this.data = data;
    }
}

ResultEnum.java (return information status enumeration class)

websocket工具类package com.crwl.chatroom.enums;

public enum ResultEnum {
    
    
    SUCCESS(0,"成功"),
    FAILURE(-1,"失败");

    private Integer code;
    private String data;

    private ResultEnum(Integer code, String data) {
    
    
        this.code = code;
        this.data = data;
    }
    public Integer getCode() {
    
    
        return code;
    }
    public void setCode(Integer code) {
    
    
        this.code = code;
    }
    public String getData() {
    
    
        return data;
    }
    public void setData(String data) {
    
    
        this.data = data;
    }
}

ChatroomController.java (receives front-end requests and returns processing results to the front-end)

package com.crwl.chatroom.controller;

import com.crwl.chatroom.dto.ChatMsg;
import com.crwl.chatroom.dto.ChatUser;
import com.crwl.chatroom.dto.Result;
import com.crwl.chatroom.enums.ChatEnum;
import com.crwl.chatroom.enums.ResultEnum;
import com.crwl.chatroom.util.WsTool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;

//在线聊天室
@RestController
@ServerEndpoint("/connect/{userBh}/{userName}")
public class ChatroomController {
    
    
    private static final Logger log = LoggerFactory.getLogger(ChatroomController.class);

    @OnOpen
    public void openSession(@PathParam("userBh")String userBh, @PathParam("userName")String userName, Session session) {
    
    
        SimpleDateFormat sdf =  new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        ChatUser chatUser = new ChatUser();
        chatUser.setUserBh(userBh);
        chatUser.setUserName(userName);
        chatUser.setOnlineTime(sdf.format(new Date()));

        WsTool.LIVING_SESSIONS_CACHE.put(userBh, session);
        WsTool.LIVING_USER_CACHE.put(userBh,chatUser);
        ChatMsg chatMsg = new ChatMsg();
        chatMsg.setSendUserBh(chatUser.getUserBh());
        chatMsg.setSendUserXm(chatUser.getUserName());
        chatMsg.setType(ChatEnum.JOIN_CHAT.getCode());
        WsTool.sendMessageAll(chatMsg);

        //刷新用户列表
        WsTool.refreshOnlineUserList();
    }

    @OnMessage
    public void onMessage(@PathParam("userBh") String userBh, String message) {
    
    
        log.info(message);
        //心跳程序
        if("HeartBeat".equals(message)){
    
    
            return;
        }
        ChatUser chatUser = WsTool.LIVING_USER_CACHE.get(userBh);
        ChatMsg chatMsg = new ChatMsg();
        chatMsg.setSendUserBh(chatUser.getUserBh());
        chatMsg.setSendUserXm(chatUser.getUserName());
        chatMsg.setType(ChatEnum.PUBLIC_MSG.getCode());
        chatMsg.setMsg(message);
        WsTool.sendMessageAll(chatMsg);
    }

    @OnClose
    public void onClose(@PathParam("userBh")String userBh, Session session) {
    
    
        ChatUser chatUser = WsTool.LIVING_USER_CACHE.get(userBh);
        //当前的Session 移除
        WsTool.LIVING_SESSIONS_CACHE.remove(chatUser.getUserBh());
        WsTool.LIVING_USER_CACHE.remove(chatUser.getUserBh());
        //并且通知其他人当前用户已经离开聊天室了
        ChatMsg chatMsg = new ChatMsg();
        chatMsg.setSendUserBh(chatUser.getUserBh());
        chatMsg.setSendUserXm(chatUser.getUserName());
        chatMsg.setType(ChatEnum.CLOSE_SOCKET.getCode());
        WsTool.sendMessageAll(chatMsg);
        //刷新用户列表
        WsTool.refreshOnlineUserList();
        try {
    
    
            session.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }

    @OnError
    public void onError(Session session, Throwable throwable) {
    
    
        try {
    
    
            session.close();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
        throwable.printStackTrace();
    }

    //一对一私聊
    @GetMapping("/privateSend/{sendUserBh}/to/{receiveUserBh}")
    public Result privateSend(@PathVariable("sendUserBh") String sendUserBh, @PathVariable("receiveUserBh") String receiveUserBh, String message) {
    
    
        Session sendSession = WsTool.LIVING_SESSIONS_CACHE.get(sendUserBh);
        Session receiveSession = WsTool.LIVING_SESSIONS_CACHE.get(receiveUserBh);
        ChatUser sendUser = WsTool.LIVING_USER_CACHE.get(sendUserBh);
        ChatUser receiver = WsTool.LIVING_USER_CACHE.get(receiveUserBh);
        ChatMsg chatMsg = new ChatMsg();
        chatMsg.setSendUserBh(sendUser.getUserBh());
        chatMsg.setSendUserXm(sendUser.getUserName());

        chatMsg.setType(ChatEnum.PRIVATE_MSG.getCode());
        chatMsg.setMsg(message);
        //对发送人发送
        WsTool.sendMessage(sendSession,  chatMsg);

        //接受人发送
        WsTool.sendMessage(receiveSession,  chatMsg);
        Result result = new Result();
        result.setCode(ResultEnum.SUCCESS.getCode());
        return result;
    }
}

4.5. Front-end implementation, the front-end uses iview framework + jquery, and the chat input box uses Baidu editor (ueditor)
chatroom.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
    <title>聊天室</title>
    <link rel="stylesheet" type = "text/css" href="../static/lib/iview/css/iview.css" />
    <link rel="stylesheet" type = "text/css" href="css/chat.css" />
    <script src="../static/js/jquery-1.8.2.min.js"></script>
    <script src="../static/lib/iview/vue.min.js"></script>
    <script src="../static/lib/iview/iview.min.js"></script>
    <script src="../static/js/common.js"></script>
    <script type="text/javascript" charset="utf-8" src="../static/lib/ueditor/ueditor.config.js"></script>
    <script type="text/javascript" charset="utf-8" src="../static/lib/ueditor/ueditor.all.js"> </script>
    <script type="text/javascript" charset="utf-8" src="../static/lib/ueditor/lang/zh-cn/zh-cn.js"></script>
    <script src="js/chatroom.js"></script>
</head>
<body>
	<Layout id="app" style="overflow-y:hidden;">
	    <Spin fix v-show="fullScreenloading">
	        <img src="../../static/image/loadm.png" class="demo-spin-icon-load ivu-icon-load-c"></img>
	        <div>{
   
   {loadMsg}}</div>
	    </Spin>
	    <row class="chatbody">
	        <row class="toolBar">
	            <i-Button type="primary" icon="ios-add" :disabled="null != ws" @click="openConnectWin()">连接服务器</i-Button>
	            <i-Button type="primary" icon="ios-add" :disabled="null == ws" @click="quitServer()">退出</i-Button>
	            <div class="clearfix"></div>
	        </row>
	        <row class="chatArea">
	            <i-col span="19" class="chatLeft">
	                <row class="chatView">
	                    <div class="msg" v-for="msg in msgList" v-html="msg"></div>
	                </row>
	                <row class="inputArea">
	                    <row class="inputForm">
	                        <script id="editor" type="text/plain" style="width:100%;height:170px;"></script>
	                        <!--<i-input :size="styleSize" type="textarea" v-model="formData.content" :rows="6"></i-input>-->
	                    </row>
	                    <row class="sendTool">
	                        <i-Button type="primary" @click="sendMsg()">发送</i-Button>
	                    </row>
	                </row>
	            </i-col>
	            <i-col span="5" class="chatUserList">
	                <div class="userTitle">
	                    <div class="title">成员列表</div>
	                    <div class="userBtnArea">
	                        <i-Button :size="styleSize" type="primary" @click="clearChooseUser()">清除选中</i-Button>
	                    </div>
	                    <div class="clearfix"></div>
	                </div>
	                <ul>
	                    <li :class="toUser==item.userBh?'selUser':''" @click="chooseUser(item.userBh)" v-for="item in chatUserList">{
   
   {item.userName}}<span class="loginDate">{
   
   {item.onlineTime}}</span></li>
	                </ul>
	            </i-col>
	        </row>
	    </row>
	
	    <Modal id="connectWin" v-model="connectModal" title="连接服务器"width="500" height="200" >
	        <i-form ref="editValidate" :model="sendUser" :rules="editRuleValidate" :label-width="80" style="padding:5px 50px 5px 20px;overflow-y: auto;" >
	            <i-col span="24">
	                <form-item label="用户ID" prop="userBh">
	                    <i-input :size="styleSize" v-model="sendUser.userBh"  ></i-input>
	                </form-item>
	            </i-col>
	            <i-col span="24">
	                <form-item label="用户昵称" prop="userName">
	                    <i-input :size="styleSize" v-model="sendUser.userName"  ></i-input>
	                </form-item>
	            </i-col>
	        </i-form>
	        <div class="clearfix"></div>
	        <div slot="footer">
	            <i-Button :size="styleSize" type="primary" @click="connectServer()">连接</i-Button>
	            <i-Button :size="styleSize" type="warning" @click="cancelFunc">取消</i-Button>
	        </div>
	    </Modal>
	</Layout>
</body>
</html>

chat.css

/*
            找到html标签、body标签,和挂载的标签
            都给他们统一设置样式
        */
html,body,#app,.el-container,.el-row{
    
    
    /*设置内部填充为0,几个布局元素之间没有间距*/
    padding: 0px;
    /*外部间距也是如此设置*/
    margin: 0px;
    /*统一设置高度为100%*/
    height: 100%;
}
.chatbody{
    
    
    height:calc(100%);
    margin:15px;
    box-shadow:2px 3px 5px #888888;
}
.toolBar{
    
    
    padding:5px 10px;
    height:45px
}
.chatArea{
    
    
    height:calc(100% - 45px);
    margin-top:0px;
    /***border-top:1px solid #ccc;***/
    padding:5px;
}
.chatLeft{
    
    
    /*border-right:1px solid #ccc;
    height:90%;*/
    height: calc(100% );
    padding:5px;
}
.chatView{
    
    
    height:calc(100% - 250px);
    /***border-bottom:1px solid #ccc;**/
    padding:5px;
    background:#fff;
    overflow-y: auto;
}

.inputArea{
    
    
    height:240px;
}
.inputForm{
    
    
    height:200px;
}
.sendTool{
    
    
    text-align:right;
    height:50px;
    line-height:50px;
}

/****
在线用户列表
 */
.chatUserList{
    
    
    /***border-bottom:1px solid #ccc;**/
    height:calc(100% - 10px);
    margin:5px 0;
    padding:5px;
    background:#fff;
}
.userTitle{
    
    
    border-bottom: 1px solid #dfdfdf;
    font-size:16px;
    padding:5px 0;
    clear:both;
}
.clearfix{
    
    
    clear:both;
}
.userTitle .title{
    
    
    float:left;
}
.userBtnArea{
    
    
    float:left;
    margin-left:10px;
    margin-top:-5px;
}
.chatUserList ul li{
    
    
    list-style-type:none;
    height:35px;
    line-height:35px;
    cursor:pointer;
    clear:both;
    padding: 0 5px;
}
.loginDate{
    
    
    float:right;
}
.selUser{
    
    
    background: #2d8cf0;
    color: #fff;
}
.iconArea{
    
    
    height:45px;
    position:relative;
}
.icon{
    
    
    height:25px;
    width:25px;
    vertical-align: middle;
    margin-top: -5px;
}
.fl{
    
    float:left;}
.fr{
    
    float:right;}
.iconDiv{
    
    
    position: absolute;
    top:45px;
    background: #fff;
    height: 160px;
    width: 370px;
    z-index: 10;
    box-shadow: 1px 2px 15px #888;
    padding: 5px;
}
.welcome{
    
    
    color: #15c02e;
}
.warning{
    
    
    color:red;
}
.msg{
    
    
    margin:10px 0;
    position:relative;
}
.userTitleOther{
    
    
    float: left;
    width: 90px;
    margin-top: 5px;
}
.otherMsgContent{
    
    
    float:left;
    margin-left: 10px;
    padding: 10px 20px;
    background: #eaeaea;
    border-radius: 5px;
}
.triangle-left {
    
    
    position: absolute;
    left: 80px;
    width: 0;
    height: 0;
    border-width: 10px;
    border-style: solid;
    border-color:#FFF #eaeaea #FFF #FFF ;
    top: 5px;
}
.userTitleSelf{
    
    
    float: right;
    width: 90px;
    margin-top: 5px;
    padding-left: 15px;
}
.selfMsgContent{
    
    
    float:right;
    margin-right: 10px;
    padding: 10px 20px;
    background: #18cb2f;
    color:#fff;
    border-radius: 5px;
}
.triangle-right {
    
    
    position: absolute;
    right: 80px;
    width: 0;
    height: 0;
    border-width: 10px;
    border-style: solid;
    border-color: #FFF #FFF #FFF #18cb2f;
    top: 5px;
}
.private{
    
    
    background: red;
    color: #fff;
    border-radius: 20px;
    padding: 3px;
}

common.js

var commonFunc ={
    
    };
var constants ={
    
    
    localCurl : "http://localhost:8090/chatroom",
    styleSize:'small'
}


commonFunc.submit=function(url,submitType, parameter, fnSuccess, fnError,contentType, Async){
    
    
    //判断是否需要同步ajax
    if (typeof (Async) == "undefined") {
    
    
        Async = true;
    };
    if(contentType == "obj"){
    
    
        parameter = JSON.stringify(parameter);
        contentType = "application/json; charset=utf-8";
    }else if(contentType == "upload"){
    
    
        //contentType = "application/x-www-form-urlencoded;charset=UTF-8";
    }else {
    
    
        contentType = "application/x-www-form-urlencoded;charset=UTF-8";
    }
    url = constants.localCurl+url;
    $.ajax({
    
    
        type: submitType,
        data: parameter,
        dataType: 'json',
        contentType:contentType,
        url: url,
        cache:false,
        beforeSend: function(XMLHttpRequest) {
    
    

        },
        crossDomain: true == !(document.all),
        xhrFields: {
    
    
            withCredentials: true
        },
        async: Async,
        success: function(data) {
    
    
            //服务器返回响应,根据响应结果,分析是否登录成功;
            var code=data.code+"";
            if (code == 1000) {
    
    
                return;
            } else if(code=="0"){
    
    
                fnSuccess(data);
            }else{
    
    
                fnError(data);
            }
        },
        complete: function (data) {
    
    
        },
        error: function (xhr, type, errorThrown) {
    
    
            //异常处理;
            fnError(xhr, type, errorThrown);
            console.log(xhr);
            console.log(type);
            console.log(JSON.stringify(errorThrown));
        }
    });
}

chatroom.js

$(function() {
    
    
    var height = $(window).height()-160;
    var _this = null;
    var vue = new Vue({
    
    
        el: '#app',
        data: {
    
    
            styleSize:constants.styleSize,
            fullScreenloading: false,
            loadMsg:'',
            ws:null,
            sendUser:{
    
    
                userBh:'',
                userName:''
            },
            formData:{
    
    
                content:''
            },
            chatUserList:[],
            msgList:[],
            toUser:'',
            timeoutObj:null,
            connectModal:false,
            editRuleValidate: {
    
    
                userBh:{
    
    required:true,message:'请输用户Id',trigger: 'blur'},
                userName:{
    
    required:true,message:'请输用户昵称',trigger: 'blur'}
            },
        },
        created:function(){
    
    
        },
        mounted :function(){
    
    
            var _this = this;
            this.connectServer();
            this.startHeart();
            this.ueditor = UE.getEditor('editor',{
    
    
                toolbars: [
                    ['emotion']
                ],
                wordCount:false,                //统计字数
                elementPathEnabled:false,       //元素路径
                enableAutoSave:false,           //自动保存
            });
            this.ueditor.ready(function() {
    
    
                $(".edui-editor-messageholder.edui-default").css({
    
     "visibility": "hidden" });
                _this.ueditor.setHeight(170);

                _this.ueditor.setContent("<span></span>")
                _this.ueditor.focus()
            });
            //监听浏览器关闭,关闭前先关闭webSocket
            window.onbeforeunload = function () {
    
    
                if(null != this.ws){
    
    
                    this.ws.close();
                }
            };
        },
        methods: {
    
    
            openConnectWin(){
    
    
                this.sendUser.userBh = "";
                this.sendUser.userName = "";
                this.connectModal = true;
            },
            cancelFunc(){
    
    
                this.connectModal = false;
            },
            connectServer(userBh, userName){
    
    
                var _this = this;
                this.$refs['editValidate'].validate((valid) => {
    
    
                    if (valid) {
    
    
                        if (null == this.ws) {
    
    
                            var urlPrefix = 'ws://localhost:8090/chatroom/connect/';
                            var url = urlPrefix + "/" + this.sendUser.userBh + "/" + this.sendUser.userName;
                            this.ws = new WebSocket(url);
                            this.connectModal = false;
                            this.ws.onopen = function () {
    
    
                                console.log("建立 websocket 连接...");
                            };
                            this.ws.onmessage = function (event) {
    
    
                                //服务端发送的消息
                                //console.log(event);
                                var data = JSON.parse(event.data);
                                //console.log(data);
                                if (null != data && (data.type == 1 || data.type == 2)) {
    
     //聊天信息
                                    var msg = data.msg;
                                    if (data.sendUserBh == _this.sendUser.userBh) {
    
    
                                        if (data.type == 1) {
    
      //公共聊天信息
                                            msg = "<div class=\"triangle-right\"></div><div class='userTitleSelf'>" + _this.sendUser.userName + "</div> " +
                                                '<div class="selfMsgContent">' + msg + '</div>';
                                        }
                                        if (data.type == 2) {
    
       //私人聊天信息
                                            msg = "<div class=\"triangle-right\"></div><div class='userTitleSelf'><span class='private'>私</span>" + _this.sendUser.userName + "</div> " +
                                                '<div class="selfMsgContent">' + msg + '</div>';
                                        }
                                        _this.msgList.push("<div class='fr'>" + msg + "</div><div class='clearfix'></div>");
                                    } else {
    
    
                                        if (data.type == 1) {
    
      //公共聊天信息
                                            msg = "<div class=\"triangle-left\"></div><div class='userTitleOther'>" + data.sendUserXm + "</div> " +
                                                '<div class="otherMsgContent">' + msg + '</div>';
                                        }
                                        if (data.type == 2) {
    
       //私人聊天信息
                                            msg = "<div class=\"triangle-left\"></div><div class='userTitleOther'>" + data.sendUserXm + "<span class='private'>私</span></div> " +
                                                '<div class="otherMsgContent">' + msg + '</div>';
                                        }
                                        _this.msgList.push("<div class='fl'>" + msg + "</div><div class='clearfix'></div>");
                                    }
                                    //+'<img src="../../static/image/happy.png" class="icon"/>'
                                }
                                //刷新在线列表
                                if (null != data && data.type == 4) {
    
    
                                    _this.chatUserList = data.onlineUserList;
                                }

                                //用户进入聊天室
                                if (null != data && data.type == 5) {
    
    
                                    msg = "<div class='welcome'>用户[" + (data.sendUserXm) + "]进入了聊天室!</div>";
                                    _this.msgList.push(msg);
                                }

                                //用户离线
                                if (null != data && data.type == 3) {
    
    
                                    msg = "<div class='warning'>用户[" + (data.sendUserXm) + "]已经离开聊天室!</div>";
                                    _this.msgList.push(msg);
                                }

                                _this.toBottom();
                            };
                            this.ws.onclose = function (event) {
    
    
                                _this.ws = null;
                                _this.chatUserList.splice(0, _this.chatUserList.length);
                                _this.toBottom();
                            }
                            this.ws.onerror = function (event) {
    
    
                                //console.log(event.data);
                                _this.ws = null;
                                _this.sendUser = {
    
    
                                    userBh: '',
                                    userName: ''
                                }
                            };
                        } else {
    
    
                            this.$Message.error("已经建立服务器连接,请不要重复连接");
                        }
                    }
                });
            },
            quitServer(){
    
    
                if(null != this.ws){
    
    
                    this.ws.close()
                    this.ws = null;
                }
            },
            toBottom(){
    
    
                setTimeout(function(){
    
    
                    $('.chatView').scrollTop($('.chatView').get(0).scrollHeight+150);
                },200)
            },
            sendMsg(){
    
    
                var _this =this;
                if(null != this.ws) {
    
    
                    var content = this.ueditor.getContent();
                    if (null == content || "" == content.trim()) {
    
    
                        this.$Message.error("请输入聊天信息");
                        return;
                    }
                    content = content.trim();
                    if(null == this.toUser || this.toUser == ""){
    
    
                        this.ws.send(content)
                    }else{
    
    
                        var sendUserBh = this.sendUser.userBh;
                        var url = "/privateSend/"+sendUserBh+"/to/"+this.toUser+"?message="+content
                        commonFunc.submit(url,"get",{
    
    },function(data){
    
    
                            if(data.code != "0"){
    
    
                                _this.$Message.error("发送信息失败");
                            }
                        },function(data){
    
    
                            _this.$Message.error("发送信息失败");
                        });
                    }
                    this.ueditor.setContent("")
                    this.ueditor.focus(true);
                    $("#ueditor_0").html("");
                }else{
    
    
                    this.$Message.error("请先点击【连接服务器】建立网络连接");
                }
            },
            chooseUser(toUser){
    
    
                if(toUser != this.sendUser.userBh){
    
    
                    this.toUser = toUser;
                }
            },
            clearChooseUser(){
    
    
                if(null != this.toUser && '' != this.toUser){
    
    
                    this.toUser='';
                }else{
    
    
                    this.$Message.error("您未选择私聊用户");
                }
            },
            startHeart: function () {
    
       //设置心跳程序,避免nginx(设置的5分钟)超时断开长连接
                var _this = this;
                this.timeoutObj && clearTimeout(this.timeoutObj);
                this.timeoutObj = setTimeout(function () {
    
    
                    //这里发送一个心跳,后端收到后,返回一个心跳消息,
                    //onmessage拿到返回的心跳就说明连接正常
                    if(null != _this.ws){
    
    
                        _this.ws.send("HeartBeat");
                        console.log('ping');
                        _this.startHeart();
                    }
                }, 5*60*1000)
            }
        }
    });
    //此处将vue对象作用域上提至window返回,用户实现ueditor的crtl+enter事件
    window.vue = vue;
});

At this point, the demo program of the entire web online chat is completed.

The shortcut key sending (crtl+enter) of the input box needs to add a piece of content in the specified position in the ueditor.all.js file, first search for "autosubmit", and add the following content in the execCommand code block

if(null != window.vue){
    
    
    window.vue.sendMsg();
}

insert image description here

5. The operation of the program

Select the project startup class ChatroomApplication.java, right-click the mouse, and click Run'ChatroomApplication' to start the project.
insert image description here
After the startup is successful, open the browser, enter http://localhost:8090/chatroom/web/chatroom.html to enter the chat room and click to
insert image description here
connect to the server, a pop-up window will pop up to connect to the server, enter the user ID and user nickname, and click [Connect] Click the button to connect to the chat server
insert image description hereinsert image description hereand directly input information, enter the shortcut key combination crtl+enter or click the [Send] button to directly send group chat messages. If you need one-on-one private chat, you can first select the one you want to chat with in the online user list on the right After the user enters the private chat information, enter the shortcut key combination of crtl+enter or click the [Send] button to privately send the chat information to the designated user
insert image description hereinsert image description here, and the web online chat program record sharing ends.

Guess you like

Origin blog.csdn.net/teamo_m/article/details/120646706