引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.29</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.35</version>
</dependency>
编写一个配置类固定
package org.hfm.signalr4j.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketTestVConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
使用类,后端,只要有用户连接就会有一个Session对象,这个对象可以用来和客户端发消息
建立连接后如果后端需要主动发送消息给前端,也就是实时数据之类的,可以使用一个while(true)循环一直循环发送,或者定时器,
循环这个map集合,因为用户退出时会自动删除这个键对应的session,
退出的用户不会推送实时数据,我的里面根据设备id来获取对应的值推送
当客户端退出时根据用户id加上对应的session删除掉他
发送实时数据时如果使用的有LocalDateTime,转换成json字符串对象时使用fastjson依赖
RedisUtil.getAllValuesWithPrefix(RealTimeContans.REDIS_KEY_EXPIRE + key + ":");是从redis中获取以某某字符串为前缀的所有值,自行找网上方法
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.json.JSONUtil;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import org.hfm.signalr4j.contans.RealTimeContans;
import org.hfm.signalr4j.entity.ThresholdEntity;
import org.hfm.signalr4j.util.RedisUtil;
import org.springframework.stereotype.Component;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* WebSocket服务
*/
@Component
@ServerEndpoint("/ws/{sid}")
public class WebSocketServer {
//存放会话对象
private static Multimap<String, Session> multimap = HashMultimap.create();
/**
* 连接建立成功调用的方法
*/
@OnOpen
public void onOpen(Session session, @PathParam("sid") String sid) {
System.out.println("设备号:" + sid + "建立连接");
multimap.put(sid, session);
try {
send();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, @PathParam("sid") String sid) {
System.out.println("收到来自客户端:" + sid + "的信息:" + message);
}
/**
* 连接关闭调用的方法
*
* @param sid
*/
@OnClose
public void onClose(@PathParam("sid") String sid,Session session) {
System.out.println("连接断开:" + sid);
multimap.remove(sid,session);
}
/**
* 消息发送
* @throws IOException
*/
public static void send() throws IOException {
if (multimap.size()<=0){
System.out.println("暂时没有连接的对象");
}
Collection<Map.Entry<String, Session>> entries = multimap.entries();
for (Map.Entry<String, Session> entry : entries) {
Session value = entry.getValue();
String key = entry.getKey();
List<String> allValuesWithPrefix = RedisUtil.getAllValuesWithPrefix(RealTimeContans.REDIS_KEY_EXPIRE + key + ":");
value.getBasicRemote().sendText(JSONUtil.toJsonStr(allValuesWithPrefix));
}
}
public static void main(String[] args) {
Multimap<String, String> multimap = HashMultimap.create();
multimap.put("1","ssss");
multimap.put("1","www");
multimap.put("1","333");
multimap.put("2","ec");
multimap.put("3","3555");
multimap.put("3","swdc");
Collection<Map.Entry<String, String>> entries = multimap.entries();
for (Map.Entry<String, String> entry : entries) {
System.out.println(entry.getValue());
System.out.println(entry.getKey());
}
}
}
发送消息的定时器
package org.hfm.signalr4j.job;
import org.hfm.signalr4j.contans.RealTimeContans;
import org.hfm.signalr4j.contorller.WebSocketServer;
import org.hfm.signalr4j.util.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
@Component
@EnableScheduling
public class WebSocketTask {
@Autowired
private WebSocketServer webSocketServer;
int i = 0;
private static final long SLEEP_TIME = 3000;
private static String accessToken;
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 通过WebSocket每隔5秒向客户端发送消息
*/
// @PostConstruct
@Scheduled(cron = "0/5 * * * * ?")
public void sendMessageToClient() throws IOException {
System.out.println( LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))+" "+ i++);
WebSocketServer.send();
}
}
使用的是谷歌 Multimap multimap = HashMultimap.create()
这个map他的值是一个集合,他的键就是一个固定的,比如用户id,固定的id对应的哪个客户端,session就是客户端,使用谷歌的集合一对多,也就是一个用户可以对应多个客户端,多个浏览器,不然新的用户会替换掉原来的map旧值,旧客户端无法接收数据
发送给前端一般以字符换为主,转换成json对象时,如果有时间类例如LocalDateTime或者Date等,使用fastJson进行转换json字符串
JSON.toJSONStringWithDateFormat(bean,"yyyy-MM-dd HH:mm:ss")
hutool工具类的最新版本只支持Date类型,使用方式
不推荐
JSONConfig jsonConfig = new JSONConfig();
jsonConfig.setDateFormat("yyyy-MM-dd");
JSONUtil.toJsonStr(tpDeclareInfoReq, jsonConfig);
测试前端
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Demo</title>
</head>
<body>
<input id="text" type="text" />
<button onclick="send()">发送消息</button>
<button onclick="closeWebSocket()">关闭连接</button>
<div id="message">
</div>
</body>
<script type="text/javascript">
var websocket = null;
//随机id
var clientId = Math.random().toString(36).substr(2);
//判断当前浏览器是否支持WebSocket
if('WebSocket' in window){
//连接WebSocket节点
websocket = new WebSocket("ws://localhost:8900/ws/"+'-1609220363435160981');
}
else{
alert('Not support websocket')
}
//连接发生错误的回调方法
websocket.onerror = function(){
setMessageInnerHTML("error");
};
//连接成功建立的回调方法
websocket.onopen = function(){
setMessageInnerHTML("连接成功");
}
//接收到消息的回调方法
websocket.onmessage = function(event){
setMessageInnerHTML(event.data);
console.log(event.data);
let json = JSON.parse(event.data);
const dataObjects = json.map(jsonString => JSON.parse(jsonString));
console.log(dataObjects);
dataObjects.forEach((item) => {
console.log(item.thresholdName+':'+ item.value+':'+item.unit);
})
}
//连接关闭的回调方法
websocket.onclose = function(){
setMessageInnerHTML("close");
}
//监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
window.onbeforeunload = function(){
websocket.close();
}
//将消息显示在网页上
function setMessageInnerHTML(innerHTML){
document.getElementById('message').innerHTML += innerHTML + '<br/>';
}
//发送消息
function send(){
var message = document.getElementById('text').value;
websocket.send(message);
}
//关闭连接
function closeWebSocket() {
websocket.close();
}
</script>
</html>