一、WebSocket简介

WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。

WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

在 WebSocket API 中,浏览器和服务器只需要做一个握手的动作,然后,浏览器和服务器之间就形成了一条快速通道。两者之间就直接可以数据互相传送。

现在,很多网站为了实现推送技术,所用的技术都是 Ajax 轮询。轮询是在特定的的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端的浏览器。这种传统的模式带来很明显的缺点,即浏览器需要不断的向服务器发出请求,然而HTTP请求可能包含较长的头部,其中真正有效的数据可能只是很小的一部分,显然这样会浪费很多的带宽等资源。

HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。

二、程序介绍

最近在做类似于电商网站的项目,其中需要用到消息推送、在线聊天的功能,本程序讲解怎么实现利用两个客户端完成在线聊天的功能,适用场景可以用于“客服聊天、多人群聊、消息推送”等。利用Vue做了一个比较简易的聊天界面,如下所示:

 三、后端实现

1.引入此次项目的目的依赖包

<!--websocket依赖-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<!-- Gson依赖 -->
<dependency><groupId>com.squareup.retrofit2</groupId><artifactId>converter-gson</artifactId><version>2.6.2</version>
</dependency>

注:WebSocket依赖是所必须的,毕竟要站在巨人的肩膀上进行开发,但其实也可以写成原生的,不需要依靠任何依赖,直接自己写底层,而Gson就不必多做介绍了,我这里主要是想将发送给前端的有效数据转成以json的形式。

2.编写WebSocket的配置类

package com.chen.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;/*** @author chen* @date 2019/10/26* @email 15218979950@163.com* @description WebSocketConfig配置类,注入对象ServerEndpointExporter,* *            这个bean会自动注册使用了@ServerEndpoint注解声明的Websocket endpoint*/
@Configuration
public class WebSocketConfig {@Beanpublic ServerEndpointExporter serverEndpointExporter() {return new ServerEndpointExporter();}
}

3.封装消息实体

package com.chen.entity;/*** @author chen* @date 2019/10/26* @email 15218979950@163.com* @description 消息实体*/
public class SocketMsg {private int type; //聊天类型0:群聊,1:单聊.private String fromUser;//发送者.private String toUser;//接受者.private String msg;//消息public int getType() {return type;}public void setType(int type) {this.type = type;}public String getFromUser() {return fromUser;}public void setFromUser(String fromUser) {this.fromUser = fromUser;}public String getToUser() {return toUser;}public void setToUser(String toUser) {this.toUser = toUser;}public String getMsg() {return msg;}public void setMsg(String msg) {this.msg = msg;}}

4.WebSocket实现类

package com.chen.service;import com.chen.entity.SocketMsg;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson;
import org.springframework.stereotype.Component;import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArraySet;/*** @author chen* @date 2019/10/26* @email 15218979950@163.com* @description websocket的具体实现类*  使用springboot的唯一区别是要@Component声明,而使用独立容器是由容器自己管理websocket的,*  但在springboot中连容器都是spring管理的。*  虽然@Component默认是单例模式的,但springboot还是会为每个websocket连接初始化一个bean,*  所以可以用一个静态set保存起来*/
@ServerEndpoint(value = "/websocket/{nickname}")
@Component
public class WebSocketService {private String nickname;private Session session;//用来存放每个客户端对应的MyWebSocket对象。private static CopyOnWriteArraySet<WebSocketService> webSocketSet = new CopyOnWriteArraySet<WebSocketService>();//与某个客户端的连接会话,需要通过它来给客户端发送数据//用来记录sessionId和该session进行绑定private static Map<String, Session> map = new HashMap<String, Session>();/*** 连接建立成功调用的方法*/@OnOpenpublic void onOpen(Session session, @PathParam("nickname") String nickname) {Map<String,Object> message=new HashMap<String, Object>();this.session = session;this.nickname = nickname;map.put(session.getId(), session);webSocketSet.add(this);//加入set中System.out.println("有新连接加入:" + nickname + ",当前在线人数为" + webSocketSet.size());//this.session.getAsyncRemote().sendText("恭喜" + nickname + "成功连接上WebSocket(其频道号:" + session.getId() + ")-->当前在线人数为:" + webSocketSet.size());message.put("type",0); //消息类型,0-连接成功,1-用户消息message.put("people",webSocketSet.size()); //在线人数message.put("name",nickname); //昵称message.put("aisle",session.getId()); //频道号this.session.getAsyncRemote().sendText(new Gson().toJson(message));}/*** 连接关闭调用的方法    */@OnClosepublic void onClose() {webSocketSet.remove(this); //从set中删除System.out.println("有一连接关闭!当前在线人数为" + webSocketSet.size());}/*** 收到客户端消息后调用的方法* @param message 客户端发送过来的消息*/@OnMessagepublic void onMessage(String message, Session session, @PathParam("nickname") String nickname) {System.out.println("来自客户端的消息-->" + nickname + ": " + message);//从客户端传过来的数据是json数据,所以这里使用jackson进行转换为SocketMsg对象,// 然后通过socketMsg的type进行判断是单聊还是群聊,进行相应的处理:ObjectMapper objectMapper = new ObjectMapper();SocketMsg socketMsg;try {socketMsg = objectMapper.readValue(message, SocketMsg.class);if (socketMsg.getType() == 1) {//单聊.需要找到发送者和接受者.socketMsg.setFromUser(session.getId());//发送者.Session fromSession = map.get(socketMsg.getFromUser());Session toSession = map.get(socketMsg.getToUser());//发送给接受者.if (toSession != null) {//发送给发送者.Map<String,Object> m=new HashMap<String, Object>();m.put("type",1);m.put("name",nickname);m.put("msg",socketMsg.getMsg());fromSession.getAsyncRemote().sendText(new Gson().toJson(m));toSession.getAsyncRemote().sendText(new Gson().toJson(m));} else {//发送给发送者.fromSession.getAsyncRemote().sendText("系统消息:对方不在线或者您输入的频道号不对");}} else {//群发消息broadcast(nickname + ": " + socketMsg.getMsg());}} catch (JsonParseException e) {e.printStackTrace();} catch (JsonMappingException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}/*** 发生错误时调用   */@OnErrorpublic void onError(Session session, Throwable error) {System.out.println("发生错误");error.printStackTrace();}/*** 群发自定义消息*/public void broadcast(String message) {for (WebSocketService item : webSocketSet) {item.session.getAsyncRemote().sendText(message);//异步发送消息.}}
}

四、前端实现

这里为了方便开发,合理地显示出效果,我使用了Element-ui框架。

<template><div><el-row :gutter="20"><el-col :span="12" :offset="6"><div class="main"><el-row><el-inputplaceholder="请输入自己的昵称"prefix-icon="el-icon-user-solid"v-model="name"style="width:50%"></el-input><el-button type="primary" @click="conectWebSocket()">建立连接</el-button><el-button type="danger">断开连接</el-button></el-row><el-row><el-inputplaceholder="请输入对方频道号"prefix-icon="el-icon-phone"v-model="aisle"style="width:40%"></el-input></el-row><el-row><el-inputplaceholder="请输入要发送的消息"prefix-icon="el-icon-s-promotion"v-model="messageValue"style="width:50%"></el-input><el-button type="primary" @click="sendMessage()">发送</el-button></el-row><div class="message"><div v-for="(value,key,index) in messageList" :key="index"><el-tag v-if="value.name==name" type="success" style="float:right">我:{{value.msg}}</el-tag><br /><el-tag v-if="value.name!=name" style="float:left">{{value.name}}:{{value.msg}}</el-tag><br /></div></div></div></el-col></el-row></div>
</template><script>
export default {data() {return {name: "", // 昵称websocket: null, // WebSocket对象aisle: "", // 对方频道号messageList: [], // 消息列表messageValue: "" // 消息内容};},methods: {conectWebSocket: function() {console.log("建立连接");if (this.name === "") {this.$alert("请输入自己的昵称", "提示", {confirmButtonText: "确定",callback: action => {}});} else {//判断当前浏览器是否支持WebSocketif ("WebSocket" in window) {this.websocket = new WebSocket("ws://localhost:8080/websocket/" + this.name);} else {alert("不支持建立socket连接");}//连接发生错误的回调方法this.websocket.onerror = function() {};//连接成功建立的回调方法this.websocket.onopen = function(event) {};//接收到消息的回调方法var that = this;this.websocket.onmessage = function(event) {var object = eval("(" + event.data + ")");console.log(object);if (object.type == 0) {// 提示连接成功console.log("连接成功");that.showInfo(object.people, object.aisle);}if (object.type == 1) {//显示消息console.log("接受消息");that.messageList.push(object);}};//连接关闭的回调方法this.websocket.onclose = function() {};//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。window.onbeforeunload = function() {this.websocket.close();};}},// 发送消息sendMessage: function() {var socketMsg = { msg: this.messageValue, toUser: this.aisle };if (this.aisle == "") {//群聊.socketMsg.type = 0;} else {//单聊.socketMsg.type = 1;}this.websocket.send(JSON.stringify(socketMsg));},showInfo: function(people, aisle) {this.$notify({title: "当前在线人数:" + people,message: "您的频道号:" + aisle,duration: 0});}}
};
</script><style scoped>
.main {position: relative;top: 20px;
}
.message {position: relative;overflow:auto;top: 20px;width: 100%;height: 40%;box-shadow: 0 2px 4px rgba(0, 0, 0, 0.12), 0 0 6px rgba(0, 0, 0, 0.04);padding: 5px;
}
</style>

SpringBoot+WebSocket+Vue整合实现在线聊天相关推荐

  1. 基于 SpringBoot+WebSocket 无DB实现在线聊天室(附源码)

    文章目录 基于 SpringBoot+WebSocket 无DB实现在线聊天室 0 项目说明 0.1 样例展示 0.2 源码地址 1 WebSocket 简介 1.1 HTTP 1.2 WebSock ...

  2. Springboot+MySQL+VUE实现的在线教育网站源码+视频教程(Springboot微服务架构)

    下载地址:https://download.csdn.net/download/BSDKT/85347133?spm=1001.2014.3001.5503 项目介绍: Springboot+MySQ ...

  3. SpringBoot SSM vue课程作业在线批阅系统

    ​​​​​​​ SpringBoot SSM vue课程作业在线批阅系统 登录 新闻公告 在线留言 课程管理 指定课代表 作业下发 作业审批 评分 在线预览 所列功能完整 使用技术: SpringBo ...

  4. Python+django网页设计入门(20):使用WebSocket创建多人在线聊天室

    2019年3月8日-10日公众号送书活动:中小学生Python课应该学什么 ================ 前导课程: Python+django网页设计入门(19):创建新模型扩展自带用户表的字段 ...

  5. WebSocket实现多人在线聊天功能

    WebSocket是现在最流行的实现多人在线聊或者私聊的技术,它可以实现客户端到客户端的通信,和以往的TCP和UDP不一样,它俩是客户端到服务端的通信,而且服务端不能直接给客户端发送消息,但是WebS ...

  6. springboot+websocket+layui制作的实时聊天室,后端开发入门样例

    实时聊天室 前言 效果图 涉及技术 springboot layui websocket 实现思路 websocket在springboot下的实现 前端实现 建立websocket连接 前端对应的w ...

  7. SpringBoot+Mybatis+Vue整合

    创建springboot项目 添加分页依赖等 <?xml version="1.0" encoding="UTF-8"?> <project ...

  8. websocket理论与实践--在线聊天室

    1.websocket理论与实践 1.1.什么是websocket协议 Websocket是HTML5开始提供的一种在单个TCP连接上进行的全双工的网络通信协议 1.2.为什么要用websocket ...

  9. springboot+websocket+vue实现订单全局弹框推送提醒(简单广播)

    写在 跟vue下 可以全局推送 不配置拦截器 在这个vue下 指向有问题 需要var that = this 概念性东西就不阐述了,直接实操一下. 在做一个项目的过程中,有遇到这样的想法,所以记录一下 ...

最新文章

  1. python执行外部命令或URL
  2. Java easycms 版本2.0发布
  3. Hacker News热文:请停止学习框架,学习领域驱动设计(DDD)(获500个点赞)
  4. Adobe illustrator 删除干扰元素 - 连载 15
  5. 使用Vscode进行Python开发环境配置
  6. linux上derby数据库,体验纯Java数据库——Derby
  7. securecrt上传下载文件命令
  8. java doc、docx、pdf格式互转
  9. Python贴吧爬虫
  10. LayaBox---背景拖动
  11. Android network基础知识 — IPv4和IPv6的区别
  12. 【Arcgis】球面坐标系转投影坐标
  13. PS2022神经滤镜Neural Filters离线安装包(PS2022/PS2021)
  14. 用C语言学习高中数学:补集
  15. python打印文档添加条码_使用Python在Excel中批量生成条形码
  16. Map.Entry和Map中的map.keySet()、map.entrySet()详解
  17. because an app is obscuring a permission request,无法开启USB调试
  18. anaconda的正确安装
  19. vimdiff及vim split
  20. 忘记了Windows系统的账号对应的密码的解决方案

热门文章

  1. 【C++】输入的几种方式
  2. JavaWeb-07-HTML学习笔记
  3. 终于有人把云原生数据库讲明白了
  4. 养牛和养羊相比,用同样的资金,哪个效益会更高一些?
  5. 关于archlinux的安装
  6. 洛谷P1914 小书童——凯撒密码
  7. RK平台5640 camera预览界面帧率过低的分析
  8. 海马汽车何以“焕新春”?
  9. 如何使用eNSP模拟器连接本地虚拟机
  10. 图解UCWEB创业故事 痛并快乐着