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: 8090servlet:context-path: /chatroomspring: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@Beanpublic 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);@OnOpenpublic 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();}@OnMessagepublic 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);}@OnClosepublic 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();}}@OnErrorpublic 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){//判断是否需要同步ajaxif (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()});//监听浏览器关闭,关闭前先关闭webSocketwindow.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="+contentcommonFunc.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在线聊天程序记录分享结束。

Java+Springboot+Websocket在线聊天室相关推荐

  1. java websocket 微服务_微服务-springboot+websocket在线聊天室

    一.引入依赖 org.springframework.boot spring-boot-starter-websocket 二.注入ServerEndpointExporter 编写一个WebSock ...

  2. 视频教程-基于Java的WebSocket的聊天室-Java

    基于Java的WebSocket的聊天室 多年 Java 企业级应用开发工作经验,曾参与中国人寿.华夏人寿.泰康人寿.信达财险等保险行业内部业务管理系统以及线上在线产品的开发:参与中国人民银行.清华大 ...

  3. SpringBoot搭建在线聊天室

    Echat-SpringBoot 一款轻量级的基于SpringBoot + WebSocket的在线聊天室项目,在MccreeFei的聊天室基础上,将其升级为SpringBoot版本,去掉了JSP文件 ...

  4. 简单两步,用Java实现网络在线聊天室

    Echat在线聊天室 一款轻量级的基于SpringBoot + WebSocket的在线聊天室项目,在MccreeFei的聊天室基础上,将其升级为SpringBoot版本,去掉了JSP文件,去掉了xm ...

  5. 嘿从零开始基于SpringBoot 打造在线聊天室(4.4W字最长博文)

    文章目录 前言 效果 主页面 消息提示 聊天页面 登录注册 前端 项目构建 依赖 项目结构 登录注册 验证码部分 登录页面 注册页面 主页面 流程 websocket loadmessage 消息发送 ...

  6. 使用 Springboot websocket 实现聊天室

    文章首发于个人博客,欢迎访问关注:https://www.lin2j.tech 通过使用spring集成的websocket和原生H5,实现一个聊天室,以此加深对websocket的了解. 文末会附带 ...

  7. phoengap–node+websocket在线聊天室

    该实验项目基于: phonegap node websocket 可以应用于android 和 ios平台. 已经测试通过.以下是测试的图:    首先是用node 架设服务器. 基本上都node 基 ...

  8. rudesocket如何使用_[WebSocket入门]手把手搭建WebSocket多人在线聊天室(SpringBoot+WebS...

    前言 本文中搭建了一个简易的多人聊天室,使用了WebSocket的基础特性. 源代码来自老外的一篇好文: 本文内容摘要: 初步理解WebSocket的前后端交互逻辑 手把手使用 SpringBoot ...

  9. SpringBoot 使用WebSocket打造在线聊天室(基于注解)

    点击上方"好好学java",选择"置顶公众号" 优秀学习资源.干货第一时间送达! 精彩内容 java实战练习项目教程 2018微服务资源springboot.s ...

最新文章

  1. 关于MySQL的酸与MVCC和面试官小战三十回合
  2. MPB:山大倪金凤组-黄翅大白蚁肠道放线菌的分离与培养
  3. NS4146 D类音频放大电路
  4. mongodb模糊查询包含特殊字符
  5. html 地址坐标图标,浏览器地址栏中显示自定义小图标
  6. “北航Clubs” Beta版本开发目标
  7. 实数系的基本定理_初中篇1|知实数-为什么0.9的循环等于1?
  8. python pandas读取txt文件_python Pandas 读取txt表格的实例
  9. 鸿蒙安卓数据互通吗,假如鸿蒙与安卓之间不能够实现游戏账号互通,你还会为其买单吗?...
  10. REBOOT Reload - 可安装在优盘的 Windows 和 DOS 启动盘
  11. c/c++ 基金会(七) 功能覆盖,虚函数,纯虚函数控制
  12. tshark 解析pcap中带TLS协议的数据包
  13. 用scrapy框架爬虫时遇到的错误ValueError: Missing scheme in request url: //scpic3.chinaz.net/Files/pic/pic 9/2021
  14. 移远BC28_opencpu方案_pin脚分配
  15. 万豪国际集团公布新任首席执行官和总裁
  16. 2021-01-20
  17. 移动端知网下文献并投屏PC端阅读
  18. 【MFC】数据库操作——ODBC(20)
  19. linux对电子信息工程专业的意义,电子信息工程专业的学生应该考哪些必要的资格证书? (1)...
  20. 产品经理经典面试题or笔试题

热门文章

  1. 炉石传说服务器维护有补偿吗,炉石传说2017年服务器故障回档补偿公告
  2. osgi框架 android,android osgi
  3. 小程序自定义navigationBar组件以及上滑修改navigationBar
  4. 【项目精选】百货中心供应链管理系统
  5. 社会主义核心价值观解码
  6. 数据库的概念模型,联系,E-R模型的设计方法
  7. python和cc哪个适合做游戏的背景音乐_好听的歌曲,适合在活动游戏环节的背景音乐...
  8. ffmpeg 打包TS介绍
  9. 起底全球收购狂,博通(Broadcom)为什么能这么豪横?
  10. 机房空调中断多久对服务器影响,机房空调故障之后,大家都惊呆了……