websocket整合springboot
1、开启WebSocket支持
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* 开启WebSocket支持
* @author zhengkai.blog.csdn.net
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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.concurrent.atomic.AtomicInteger;
/**
* @Author:JCccc
* @Description:
* @Date: created in 15:56 2019/5/13
*/
@Component
@ServerEndpoint(value = "/websocket/{userId}")
public class WebSocket {
private Logger logger = LoggerFactory.getLogger(this.getClass());
/**
* 在线人数
*/
private AtomicInteger onlineNumber = new AtomicInteger(0);
/**
* 会话
*/
private Session session;
/**
* 用户
*/
private String userID;
/**
* 建立连接
*
* @param session
*/
@OnOpen
public void onOpen(@PathParam("userId") String userId, Session session) {
//把会话存到连接池中
this.session=session;
this.userID=userId;
onlineNumber.incrementAndGet();
System.out.println("有新连接加入!当前在线人数为" + onlineNumber.get() +" 当前session是" + session.hashCode()+" 用户id为: "+userId);
SessionPool.sessions.put(userId,session);
}
@OnError
public void onError(Session session, Throwable error) {
logger.info("服务端发生了错误"+error.getMessage());
error.printStackTrace();
}
/**
* 连接关闭
*/
@OnClose
public void onClose(Session session) throws IOException {
SessionPool.close(session.getId());
session.close();
System.out.println("websocket关闭成功!");
}
/*
* 收到客户端消息后调用的方法
* */
@OnMessage
public void onMessage(String message, Session session){
try {
if(StringUtils.isNotBlank(message)){
//解析为JSON文件防止有个篡改
System.out.println(message);
/*JSONObject jsonObject = JSON.parseObject(message);
//追加发送人(防止串改)
jsonObject.put("senderId",this.userID);
//抽离接收人
String recipient=jsonObject.getString("receptionPerson");
SessionPool.sendMessage(this.userID,jsonObject.toJSONString());
if(StringUtils.isNotBlank(recipient)&&SessionPool.sessions.containsKey(recipient)){
if(jsonObject.get("code").equals(1)){
}else {
};
*/
SessionPool.sendMessage("小和尚",message);
}else {
logger.info("--用户不在线");
}
}catch (Exception e){
e.printStackTrace();
}
}
}
import org.springframework.stereotype.Component;
import javax.websocket.Session;
import java.io.IOException;
import java.time.LocalDate;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SessionPool {
/*以用户的姓名为key,WebSocket为对象保存起来*/
public static Map<String, Session> sessions = new ConcurrentHashMap<String, Session>();
/*关闭连接 判断session是否为空,如果不是空的那就把他给关闭了*/
public static void close(String sessionId) throws IOException{
for (String userID: sessions.keySet()) {
Session session= sessions.get(userID);
if(session.getId().equals(sessionId)){
sessions.remove(userID);
System.out.println(userID+"用户: "+LocalDate.now()+" 退出");
break;
}
}
}
/*发送消息 查询找到用户然后同步发送消息*/
public static void sendMessage(String userId,String message){
sessions.get(userId).getAsyncRemote().sendText(message);
}
/*循环在线的每一个用户给他们发送消息*/
public static void sendMessage(String message){
for (String sessionId:SessionPool.sessions.keySet()){
/*循环key发送消息*/
SessionPool.sessions.get(sessionId).getAsyncRemote().sendText(message);
}
}
}
uve前台监听
var websock = null;
var timer = null;
var callableMsg = null;
/**
* 初始化websocket连接
*/
function initWebSocket(e){ //初始化weosocket
const wsuri = "ws://127.0.0.1:8022/websocket/"+e;
websock = new WebSocket(wsuri);
websock.onmessage = websocketonmessage;
websock.onopen = websocketonopen;
websock.onerror = websocketonerror;
websock.onclose = websocketclose;
}
/**
* websocket连接成功以后
*/
let websocketonopen = ()=>{
let that = this;
websock.send("websocket连接成功!");
//连接后,定时发送,否则不段时间不通信会自动断连(时间长短一般是服务端指定的)
timer = setInterval(function () {
console.log("定时任务")
websock.send("websocket连接成功!")
}, 4000);
}
/**
* wobsocket连接错误
*/
let websocketonerror = ()=>{//连接建立失败重连
this.initWebSocket();
}
/**
* websocket监听数据
*/
let websocketonmessage = (e)=>{ //数据接收
window.dispatchEvent(new CustomEvent('onmessageWS', {
detail: {
data: e.data
}
}));
}
/**
* websocket发送数据
*/
function socketsend(e){//数据发送
websock.send(e);
}
/**
* 关闭websocket连接
*/
let websocketclose = (e)=>{ //关闭
clearInterval(timer);
websock.close();
console.log('websocket连接断开'+e);
}
// 导出
export { initWebSocket, socketsend, websocketclose, websocketonmessage}
<template>
<div class="loginTop">
<div class="log">
<div class="logStyle">
</div>
<div class="shopName">
上海身腾电商管理系统
</div>
</div>
<div class="login">
<div class="login_info" v-if="status">
<div class="scan-code">
<div class="scan" @click="sanCode">
<div class="code">
<div class="svgStyle">
<i class="el-icon-thumb"></i>
</div>
<div class="codeStyle">
扫码登录
</div>
</div>
</div>
</div>
<div class="loginPass">
<el-form :model="ruleForm" :rules="rules" ref="ruleForms" size="small" label-width="120px" class="demo-ruleForm">
<el-form-item>
<div class="passStyle">
密码登录
</div>
</el-form-item>
<el-form-item label="用户名: " prop="name">
<el-input v-model="ruleForm.name" prefix-icon="el-icon-user-solid"></el-input>
</el-form-item>
<el-form-item label="密码: " prop="region">
<el-input type="password" v-model="ruleForm.region" prefix-icon="el-icon-key"></el-input>
</el-form-item>
<el-form-item>
<div class="login-btn" @click="loginBtn">
登录
</div>
<div class="opera" style="text-align: right">
<spa class="operation" @click="resetForm">
忘记密码
</spa>
<span class="operation" @click="forgotPass">
忘记登录名
</span>
</div>
</el-form-item>
</el-form>
</div>
</div>
<div class="scan_codes" v-else>
<div class="scan-code">
<div class="scan" @click="sanCode">
<div class="code">
<div class="svgStyle">
<i class="el-icon-thumb"></i>
</div>
<div class="codeStyle">
密码登录
</div>
</div>
</div>
</div>
<div class="loginPass">
<el-form label-width="105px" class="demo-ruleForm">
<el-form-item>
<div class="passStyle">
请登录升腾小程序 扫描二维码登录
</div>
</el-form-item>
<el-form-item>
<div class="login-btn">
<div class="codes" @click="flushCodeImg">
<img class="imageStyle" :src="imageRul">
</div>
</div>
</el-form-item>
</el-form>
</div>
</div>
</div>
</div>
</template>
<script>
import { useRouter } from 'vue-router'
import { reactive, ref, getCurrentInstance } from 'vue'
import { ElMessage } from 'element-plus'
import network from "../network/index.js"
import { initWebSocket, websocketsend, websocketclose, websocketonmessage } from "../utils/websocket.js";
//注:别忘了引入axios
export default{
setup(){
/*存储用户名密码等数据*/
const ruleForm = reactive({
name: '',
region: ''
})
let imageRul = ref()
const ruleForms = ref()
const router = useRouter()
//校验用户名和密码事件 from表单校验事件
const rules = {
name:[
{required: true, message: '请输入用户名', trigger: 'blur'},
],
region: [
{ required: true, message: '请输入密码', trigger: 'blur' }
]
}
//刷新二维码
const flushCodeImg = () =>{
let xhr = new XMLHttpRequest()
xhr.open('GET', "http://localhost:8022/user/createCodeImg", true)
xhr.responseType = 'blob' // 关键的一步
xhr.onreadystatechange = function() {
let uuid = xhr.getResponseHeader('uuid');
if (xhr.readyState == 4) {
if (xhr.status == 200) {
let blob = this.response
// 将blob转化为base64形式
let reader = new FileReader()
reader.readAsDataURL(blob)
reader.onloadend = function() {
let base64data = reader.result // 这里base64data就是请求到的图片的base64码
imageRul.value = base64data
initWebSocket(uuid);
}
}
}
}
xhr.send()
}
//校验用户点击登录状态 切换二维码登录和密码登录
let status = ref('true')
//用户点击切换按钮事件
const sanCode = () =>{
if(status.value){
let xhr = new XMLHttpRequest()
xhr.open('GET', "http://localhost:8022/user/createCodeImg", true)
xhr.responseType = 'blob' // 关键的一步
xhr.onreadystatechange = function() {
let uuid = xhr.getResponseHeader('uuid');
if (xhr.readyState == 4) {
if (xhr.status == 200) {
let blob = this.response
// 将blob转化为base64形式
let reader = new FileReader()
reader.readAsDataURL(blob)
reader.onloadend = function() {
let base64data = reader.result // 这里base64data就是请求到的图片的base64码
imageRul.value = base64data
initWebSocket(uuid);
}
}
}
}
xhr.send()
}
if(status.value == false){
console.log("关闭")
websocketclose();
}
status.value = !status.value
}
//表单清空事件 忘记密码
const resetForm = () => {
// ruleForms.value.resetFields()
router.push({
path: '/discover'
})
}
const forgotPass = () =>{
router.push({
path: '/forgotPass'
})
}
//登录按钮事件
const loginBtn = async() =>{
/*
* 校验
* */
ruleForms.value.validate((valid) => {
if(valid){
const { name,region} = ruleForm
// eslint-disable-next-line no-unused-vars
let params={
username: name,
password: region
}
network.login(params).then((res) =>{
if(res.data.resultCode == 40001){
console.log(res.data.desc)
ElMessage({
showClose: true,
type: 'error',
message: res.data.desc
})
return
}
if(res.data.resultCode == 10000){
localStorage.setItem("token",res.data.data) //客户端存储token
router.push({
name: 'menuIndex' //跳转至主页面
})
ElMessage({
showClose: true,
type: 'success',
message: '登录成功'
})
return
}
})
}else{
ElMessage({
showClose: true,
type: 'error',
message: '请输入用户名或密码'
})
return;
}
})
}
// 创建接收消息函数
const getSocketData = e => {
const data = e && e.detail.data
//这边编写处理服务端消息代码
console.log("接收消息: "+data)
}
//注册监听事件
window.addEventListener('onmessageWS', getSocketData)
return{
ruleForm,
sanCode,
status,
loginBtn,
ruleForms,
rules,
resetForm,
forgotPass,
imageRul,
flushCodeImg
}
}
}
</script>
<style lang="less" scoped>
::v-deep(.login .login_info .loginPass .el-form .el-form-item .el-form-item__content .el-input .el-input__prefix){
line-height: 47px !important;
}
.loginTop {
width: 100%;
height: 100%;
background-size: 100% 100%;
background-image: url(../assets/static/jhk-1634365898144.jpg);
position: relative;
.log{
width: 100%;
height: 90px;
display: flex;
background-color: #53c68c;
text-indent:25px;
.logStyle{
width: 80px;
height: 80px;
padding-top: 5px;
border-radius: 50%;
img{
width: 80px;
height: 80px;
border-radius: 50%;
}
}
.shopName{
height: 400px;
line-height: 90px;
padding-left: 15px;
letter-spacing: 5px;
color: black;
font-size: 26px;
}
}
.login{
width: 670px;
height: 480px;
position: absolute;
left: 54%;
top: 38%;
background-color: #ffffff;
box-shadow: 0 0 50px rgb(228, 231, 231);
.scan_codes{
width: 100%;
height: 100%;
.scan-code {
width: 100%;
height: 66px;
position: relative;
.scan {
width: 148px;
height: 18px;
position: absolute;
right: 2px;
top: 32px;
.code {
width: 100%;
height: 100%;
cursor: pointer;
.svgStyle {
width: 21px;
height: 18px;
float: left;
}
.codeStyle {
width: 120px;
height: 18px;
height: 18px;
float: left;
}
}
}
}
.loginPass{
::v-deep(.el-input__inner){
height: 48px;
}
::v-deep(.el-form-item){
margin-top: 35px;
width: 580px;
}
::v-deep(.el-form-item .passStyle){
cursor: default;
border-bottom: 1px solid #e7e4e6;
font-size: 15px;
}
::v-deep(.el-form-item .login-btn){
height: 100%;
width: 100%;
}
::v-deep(.el-form-item .login-btn .codes){
height: 220px;
width: 230px;
margin: 0px auto;
cursor: pointer;
}
::v-deep(.el-form-item .login-btn .codes .imageStyle){
height: 100%;
width: 100%;
}
}
}
.login_info{
width: 100%;
height: 100%;
.scan-code{
width: 100%;
height: 66px;
position: relative;
.scan{
width: 148px;
height: 18px;
position: absolute;
right: 2px;
top: 32px;
.code{
width: 100%;
height: 100%;
cursor: pointer;
.svgStyle{
width: 21px;
height: 18px;
float: left;
}
.codeStyle{
width: 120px;
height: 18px;
height: 18px;
float: left;
}
}
}
}
.loginPass{
::v-deep(.el-form){
margin-top: 0px;
}
::v-deep(.el-input__inner){
height: 48px;
}
::v-deep(.el-form-item){
margin-top: 35px;
width: 580px;
}
::v-deep(.el-form-item .passStyle){
cursor: default;
border-bottom: 1px solid #e7e4e6;
font-size: 15px;
}
::v-deep(.el-form-item .login-btn){
height: 48px;
background-color: #42b983;
line-height: 48px;
font-size: 15px;
text-align:center;
cursor: pointer;
letter-spacing: 3px;
}
::v-deep(.el-form-item .opera .operation){
margin-left: 23px;
color: #b3b3b3;
font-size: 0.6em;
cursor: default;
}
::v-deep(.el-form-item__label){
line-height: 48px;
}
}
}
}
}
</style>