直接上代码
后端:
首先建一个 Result 类
package com.ljv.chat.util;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
public class Result {
public String clientId;
public long timestamp;
public SseEmitter sseEmitter;
public Result(String clientId, long timestamp, SseEmitter sseEmitter) {
this.clientId = clientId;
this.timestamp = timestamp;
this.sseEmitter = sseEmitter;
}
}
然后创建一个 SseMap 类
package com.ljv.chat.util;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SseMap {
public static final Map<String, Result> sseEmitterMap = new ConcurrentHashMap<>();
}
创建 SaticScheduleTask 类 定时任务
package com.ljv.chat.util;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class SaticScheduleTask {
//3.添加定时任务
// @Scheduled(cron = "0 0 3 * * ? ")
@Scheduled(cron = "0 0 3 * * ? ")
private void configureTasks() {
SseMap.sseEmitterMap.clear();
}
}
创建 SseEmitterController
package com.ljv.chat.test.controller;
import com.ljv.chat.util.Result;
import com.ljv.chat.util.SseMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 服务器端实时推送技术之 SseEmitter 的用法测试
* <p>
* 测试步骤:
* 1.请求http://localhost:8888/sse/start?clientId=111接口,浏览器会阻塞,等待服务器返回结果;
* 2.请求http://localhost:8888/sse/send?clientId=111接口,可以请求多次,并观察第1步的浏览器返回结果;
* 3.请求http://localhost:8888/sse/end?clientId=111接口结束某个请求,第1步的浏览器将结束阻塞;
* 其中clientId代表请求的唯一标志;
*
* @author
*/
@CrossOrigin
@RestController
@RequestMapping("/web/sse")
public class SseEmitterController {
private static final Logger logger = LoggerFactory.getLogger(SseEmitterController.class);
// 用于保存每个请求对应的 SseEmitter
/**
* 返回SseEmitter对象
*
* @param clientId
* @return
*/
@RequestMapping("/start")
public SseEmitter testSseEmitter(String clientId) {
// 默认30秒超时,设置为0L则永不超时
SseEmitter sseEmitter = new SseEmitter(0L);
SseMap.sseEmitterMap.put(clientId, new Result(clientId, System.currentTimeMillis(), sseEmitter));
return sseEmitter;
}
/**
* 向SseEmitter对象发送数据
*
* @param clientId
* @return
*/
@RequestMapping("/send")
public String setSseEmitter(String clientId) {
try {
Result result = SseMap.sseEmitterMap.get(clientId);
if (result != null && result.sseEmitter != null) {
long timestamp = System.currentTimeMillis();
String re = "{\"success\":true,\"message\":\"操作成功!\",\"code\":200,\"result\":{\"id\":1018},\"timestamp\":1629254216358}";
result.sseEmitter.send(re);
}
} catch (IOException e) {
logger.error("IOException!", e);
return "error";
}
return "Succeed!";
}
/**
* 将SseEmitter对象设置成完成
*
* @param clientId
* @return
*/
@RequestMapping("/end")
public String completeSseEmitter(String clientId) {
Result result = SseMap.sseEmitterMap.get(clientId);
if (result != null) {
SseMap.sseEmitterMap.remove(clientId);
result.sseEmitter.complete();
}
return "Succeed!";
}
}
创建 ChatController
@CrossOrigin //解决跨域
@RestController
@RequestMapping("chat")
public class ChatController {
/**
* 发送消息
* @param homeId 房间id
* @param msg 发送的消息
* @param userId 用户id
* @return
*/
@GetMapping("send")
public Result2<?> send(String homeId,String msg,String userId){
for (String key : SseMap.sseEmitterMap.keySet()) {
if(key.indexOf(String.valueOf(homeId))!=-1){
try{
Result result = SseMap.sseEmitterMap.get(key);
Map map=new HashMap();
map.put("msg",msg);
map.put("userId",userId);
String string = JSON.toJSONString(map);
result.sseEmitter.send(string);
}catch (Exception e){
SseMap.sseEmitterMap.remove(key);
}
}
}
return Result2.OK("发送成功");
}
}
前端(uniapp写的) 建立连接和关闭都是一样的,样式就是简单的测试使用
<template>
<view class="content">
<image class="logo" src="/static/logo.png"></image>
<view class="text-area">
<!-- <navigator :url="'/pages/index/chat?id='+homeId" hover-class="navigator-hover"> -->
<button @click="join()"><text class="title">加入{{homeId}}房间</text></button>
<!-- </navigator> -->
<button @click="exit()"><text class="title">退出房间</text></button>
<input v-if="send" type="text" v-model="msg" placeholder="请输入信息" style="border: 1rpx #4CD964 solid; border-radius: 10rpx;" />
<button @click="sendMsg()">{{title}}</button>
</view>
<view v-if="getMsg!=null && getMsg!='' && !isMe">收到消息:<text style="color: red;">{{getMsg}}</text></view>
<view v-if="getMsg!=null && getMsg!='' && isMe">发送消息:<text style="color: red;">{{getMsg}}</text> 成功</view>
</view>
</template>
<script>
export default {
data() {
return {
title: '发送',
randNum: '',
msg:'',
homeId: '1000',
send: true,
getMsg: '',
isMe: false,
source:null,
}
},
onLoad() {
},
onShow() {
},
onHide() {
},
methods: {
sendMsg(){
// alert("1")
let self = this
self.$http.get('/chat/send', {
homeId: self.homeId,
msg: self.msg,
userId: self.randNum
}).then((res) => {
console.log(res.data)
});
},
exit(){
// alert("2")
this.close1();
},
join(){
this.auto();
// this.send=true
},
/**
* 关闭连接
*/
close1() {
let self = this
self.$http.get('/chat/end', {
clientId: self.randNum
}).then((
res) => {
self.source.close();
uni.showToast({
title: '已退出房间',
duration: 2000
});
});
},
/**
* 与服务器端建立连接
*/
auto() {
let self = this
if (!!window.EventSource) {
this.randNum = this.homeId + this.uuid()
self.source = new EventSource('http://localhost:8989/chat/start?clientId=' + this
.randNum);
uni.showToast({
title: '加入房间成功',
duration: 2000
});
self.source.onopen = function(event) {
console.log("链接建立")
};
self.source.addEventListener('message', function(e) {
let data = JSON.parse(e.data)
console.log(data)
self.getMsg=data.msg
self.isMe=data.userId==self.randNum?true:false
// alert(data.message)
}, false);
self.source.addEventListener('error', function(e) {
console.log(e)
});
} else {
alert("不支持sse")
}
},
uuid() {
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = "-";
var uuid = s.join("");
return uuid;
},
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>