依赖 pom.xml
org.springframework.boot
spring-boot-starter-websocket
cn.hutool
hutool-all
5.6.0
org.bouncycastle
bcprov-jdk15to18
1.68
com.alibaba
fastjson
1.2.7
websocket主要实现分为两个步骤:
一,websocket的一个配置类 谨记在这里已经加了@EnableWebSocket启动类就不用加了
,代码如下
//注意注解
@EnableWebSocket
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
或者不用单独新建一个配置类直接把这个bean方法放在我们启动类里面也行(注意:放启动类我们的@EnableWebSocket需要带上,@Configuration这个就可以不要了)
二,websocket的以一个调用接口或者controller层(我写了两种controller,我用的第一种)
//注意是controller注解
@Controller
@ServerEndpoint("/websocket/{userId}")
public class WebSocket {
/**concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。*/
private static ConcurrentHashMap webSocketMap = new ConcurrentHashMap();
/**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
private Session session;
/**接收userId*/
private String userId="";
/**
* 连接建立成功调用的方法*/
@OnOpen
public void onOpen(Session session,@PathParam("userId") String userId) {
this.session = session;
this.userId=userId;
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
webSocketMap.put(userId,this);
}else{
webSocketMap.put(userId,this);
}
System.out.println("当前连接用户:"+userId);
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
if(webSocketMap.containsKey(userId)){
webSocketMap.remove(userId);
}
System.out.println("用户 "+userId+" 退出:");
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("收到用户消息:"+userId+",报文:"+message);
if(StrUtil.isNotBlank(message)){
try {
//解析发送的报文
JSONObject jsonObject = JSON.parseObject(message);
//追加发送人(防止串改)
jsonObject.put("fromUserId",this.userId);
String toUserId=jsonObject.getString("toUserId");
//传送给对应toUserId用户的websocket
if(StrUtil.isNotBlank(toUserId) && webSocketMap.containsKey(toUserId)){
webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString());
}else{
webSocketMap.get(this.userId).sendMessage("请求的userId:"+toUserId+"不在该服务器上");
System.out.println("请求的userId:"+toUserId+"不在该服务器上");
}
}catch (Exception e){
e.printStackTrace();
}
}
}
/**
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("用户错误:"+this.userId+",原因:"+error.getMessage());
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 发送自定义消息
* */
public void sendInfo(String message,String userId) throws IOException {
System.out.println("发送消息到:"+userId+",报文:"+message);
if(StrUtil.isNotBlank(userId) && webSocketMap.containsKey(userId)){
webSocketMap.get(userId).sendMessage(message);
}else{
System.out.println("用户"+userId+",不在线!");
}
}
public static ConcurrentHashMap getWebSocketMap() {
return webSocketMap;
}
public static void setWebSocketMap(ConcurrentHashMap webSocketMap) {
WebSocket.webSocketMap = webSocketMap;
}
}
第二种webcontroller ,其实两种方式大差不差的都能用
//注意是controller注解
@Controller
@ServerEndpoint("/websocket/{userId}")
public class WebSocket {
/* public static CopyOnWriteArraySet webSockets = new CopyOnWriteArraySet<>();
*//**
* concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
*//*
private static ConcurrentHashMap webSocketMap = new ConcurrentHashMap();
*//**
* 与某个客户端的连接会话,需要通过它来给客户端发送数据
*//*
private Session session;
*//**
* 接收userId
*//*
private String userId = "";
private static Map sessionPool = new HashMap();
@OnOpen
public void onOpen(Session session, @PathParam(value = "tableId") String code) {
this.session = session;
webSockets.add(this);
sessionPool.put(code, session);
// Constants.WEBSOCKET = true;//定义常量 是否开启websocket连接
System.out.println("【websocket消息】有新的连接,总数为:" + webSockets.size());
}
@OnClose
public void onClose() {
webSockets.remove(this);
//Constants.WEBSOCKET = false;
System.out.println("【websocket消息】连接断开,总数为:" + webSockets.size());
}
@OnMessage
public void onMessage(String message) {
System.out.println("【websocket消息】收到客户端消息:" + message);
}
// 此为广播消息
public void sendAllMessage(String message) {
for (WebSocket webSocket : webSockets) {
System.out.println("【websocket消息】广播消息:" + message);
try {
webSocket.session.getAsyncRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// 此为单点消息
public static void sendOneMessage(String code, String message) {
Session session = sessionPool.get(code);
System.out.println(code);
*//*在发送数据之前先确认 session是否已经打开 使用session.isOpen() 为true 则发送消息
* 不然会报错:The WebSocket session [0] has been closed and no method (apart from close()) may be called on a closed session *//*
if (session != null && session.isOpen()) {
try {
session.getAsyncRemote().sendText(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}
*//**
* 发送自定义消息
* *//*
public static void sendInfo(String message,String userId) throws IOException {
System.out.println("发送消息到:"+userId+",报文:"+message);
if(StrUtil.isNotBlank(userId) && webSocketMap.containsKey(userId)){
webSocketMap.get(userId).onMessage(message);
}else{
System.out.println("用户"+userId+",不在线!");
}
}
public static ConcurrentHashMap getWebSocketMap() {
return webSocketMap;
}
public static void setWebSocketMap(ConcurrentHashMap webSocketMap) {
WebSocket.webSocketMap = webSocketMap;
}
*/
}
三,下面这个才是重点,我的项目需求需要通过实时采集数据,采集到指定数量了就需要停止采集所以我用的线程来开启和关闭定时任务,定时任务我是通过手动调用来触发的。直接上测试代码:
@Component
@Slf4j
public class Jdkinvok {
@Autowired
WebSocket webSocket;
public void timer5(String code) { //这里的code需要和你websocket的userId相同不然不会实时推送数据
ScheduledThreadPoolExecutor scheduled = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(1);
// ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(10);
scheduled.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
String message = "这里可以换成其他条件";
try {
if (message != "") { //这里可以更具自己需求换对应的数据采集方式
webSocket.sendInfo(message + "", code);
scheduled.shutdown(); //采集到了数据我就关闭线程来停止定时任务
} else {
webSocket.sendInfo("", code);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}, 0, 5, TimeUnit.SECONDS);
}
}
完事!!!不求关注和三连操作。只希望能帮助和我一样菜的人早日走上正轨