Java+Springboot+Websocket在线聊天室

1、什么是websocket?

websocket是由HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。它是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

2、为啥要实现web在线聊天程序?

最近在学习过程中看到了websocket协议,于是就有了一个想法想使用springboot+websocket实现一个web在线聊天的demo。

花了一点时间,边学习边搭建,最终实现了下面的一个简单的web在线聊天的demo,特在这里记录一下。既是为了分享给大家,也是为了给自己的学习留下一个痕迹,用以不断夯实自己的技术能力。

3、实现结果

先上几个截图,提前展示一下实现结果
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

4、实现过程

4.1、创建一个springboot项目,命名为chat,并在pom.xml文件中添加需要的maven依赖

<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、添加yml配置信息

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

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

4.3、修改springboot启动类

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、准备websocket的工具类,dto类以及enum类
WsTool .java(websocket工具类)

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(聊天信息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(聊天用户信息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 (封装Http请求的返回对象Dto)

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(聊天类型枚举类)

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(返回信息状态枚举类)

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(接收前端请求,并将处理结果返回前端)

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、前端实现,前端采用iview框架+jquery,聊天输入框使用百度编辑器(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;
});

到此,整个web在线聊天的demo程序就搭建完成。

其中输入框的快捷键发送(crtl+enter)需要在ueditor.all.js文件中的指定位置增加一段内容,先搜索“autosubmit”,在execCommand代码块中加入以下内容

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

在这里插入图片描述

5、程序的运行

选中项目启动类ChatroomApplication.java,右键鼠标,单击Run’ChatroomApplication’启动项目。
在这里插入图片描述
启动成功后,打开浏览器,输入http://localhost:8090/chatroom/web/chatroom.html即可进入聊天室
在这里插入图片描述
点击连接服务器,弹出连接服务器弹窗,输入用户Id以及用户昵称,点击【连接】按钮即可以连接进聊天服务器
在这里插入图片描述在这里插入图片描述直接输入信息,快捷键输入crtl+enter组合键或者点击【发送】按钮直接群发聊天信息,如果需要一对一私聊,可以先在右边在线用户列表中选择需要聊天的用户后输入私聊信息,快捷键输入crtl+enter组合键或者点击【发送】按钮向指定用户私人发送聊天信息
在这里插入图片描述在这里插入图片描述到此,web在线聊天程序记录分享结束。

猜你喜欢

转载自blog.csdn.net/teamo_m/article/details/120646706
今日推荐