Gitee 项目地址
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
创建配置文件
然后创建一个配置文件,注入ServerEndpointExporter,简单来说就是让SpringBoot识别Websocket的注解
package com.oy.websocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* WebSocket服务配置
* @Author ouyangtengfei
* @Date 2022/3/16 09:08
*/
@Configuration
public class WebsocketConfig {
/**
* 注入一个ServerEndpointExporter
* 该Bean会自动注册使用@ServerEndpoint注解申明的websocket endpoint
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
构建消息模板类
package com.oy.websocket.dto;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
/**
* @Author ouyangtengfei
* @Date 2022/3/16 09:09
*/
@Data
@ToString
@NoArgsConstructor
public class WebsocketMsgDTO {
/**
* 发送消息用户
*/
private String uid;
/**
* 接收消息用户
*/
private String toUId;
/**
* 消息内容
*/
private String content;
/**
* 消息时间
*/
private String dateTime;
/**
* 用户列表
*/
private List onlineUser;
/**
* 统一消息模版
* @param uid 发送消息用户
* @param content 消息内容
* @param onlineUser 在线用户列表
*/
public WebsocketMsgDTO(String uid, String content, List onlineUser) {
this.uid = uid;
this.content = content;
this.onlineUser = onlineUser;
this.dateTime = localDateTimeToString();
}
/**
* 获取当前时间
* @return String 12:00:00
*/
private String localDateTimeToString() {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
return dateTimeFormatter.format( LocalDateTime.now());
}
}
最后上服务端逻辑代码
@ServerEndpoint(value="") 这个是Websocket服务url前缀,{uid}类似于ResutFul风格的参数
package com.oy.websocket.config;
import com.alibaba.fastjson.JSONObject;
import com.oy.websocket.dto.WebsocketMsgDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Component;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Author ouyangtengfei
* @Date 2022/3/16 09:11
*/
@ServerEndpoint(value = "/webSocket/{uid}")
@Component
@Slf4j
public class WebSocketServer {
/**
* 机器人发言名称
*/
private static final String SPOKESMAN_ADMIN = "机器人";
/**
* concurrent包的线程安全Set
* 用来存放每个客户端对应的Session对象
*/
private static final ConcurrentHashMap SESSION_POOLS = new ConcurrentHashMap<>();
/**
* 静态变量,用来记录当前在线连接数。
* 应该把它设计成线程安全的。
*/
private static final AtomicInteger ONLINE_NUM = new AtomicInteger();
/**
* 获取在线用户列表
* @return List
*/
private List getOnlineUsers() {
return new ArrayList<>(SESSION_POOLS.keySet());
}
/**
* 用户建立连接成功调用
* @param session 用户集合
* @param uid 用户标志
*/
@OnOpen
public void onOpen(Session session, @PathParam(value = "uid") String uid) {
// 将加入连接的用户加入SESSION_POOLS集合
SESSION_POOLS.put(uid, session);
// 在线用户+1
ONLINE_NUM.incrementAndGet();
sendToAll(new WebsocketMsgDTO(SPOKESMAN_ADMIN, uid + " 加入连接!", getOnlineUsers()));
}
/**
* 用户关闭连接时调用
* @param uid 用户标志
*/
@OnClose
public void onClose(@PathParam(value = "uid") String uid) {
// 将加入连接的用户移除SESSION_POOLS集合
SESSION_POOLS.remove(uid);
// 在线用户-1
ONLINE_NUM.decrementAndGet();
sendToAll(new WebsocketMsgDTO(SPOKESMAN_ADMIN, uid + " 断开连接!", getOnlineUsers()));
}
/**
* 服务端收到客户端信息
* @param message 客户端发来的string
* @param uid uid 用户标志
*/
@OnMessage
public void onMessage(String message, @PathParam(value = "uid") String uid) {
log.info("Client:[{}], Message: [{}]", uid, message);
// 接收并解析前端消息并加上时间,最后根据是否有接收用户,区别发送所有用户还是单个用户
// WebsocketMsgDTO msgDTO = JSONObject.parseObject(message, WebsocketMsgDTO.class);
// msgDTO.setDateTime(localDateTimeToString());
WebsocketMsgDTO msgDTO = new WebsocketMsgDTO();
msgDTO.setDateTime(localDateTimeToString());
msgDTO.setContent(message);
msgDTO.setToUId(uid);
// 如果有接收用户就发送单个用户
if (Strings.isNotBlank(msgDTO.getToUId())) {
sendMsgByUid(msgDTO);
return;
}
// 否则发送所有人
sendToAll(msgDTO);
}
/**
* 给所有人发送消息
* @param msgDTO msgDTO
*/
private void sendToAll(WebsocketMsgDTO msgDTO) {
//构建json消息体
String content = JSONObject.toJSONString(msgDTO);
// 遍历发送所有在线用户
SESSION_POOLS.forEach((k, session) -> sendMessage((Session) session, content));
}
/**
* 给指定用户发送信息
*/
private void sendMsgByUid(WebsocketMsgDTO msgDTO) {
sendMessage((Session) SESSION_POOLS.get(msgDTO.getToUId()), JSONObject.toJSONString(msgDTO));
}
/**
* 发送消息方法
* @param session 用户
* @param content 消息
*/
private void sendMessage(Session session, String content){
try {
if (Objects.nonNull(session)) {
// 使用Synchronized锁防止多次发送消息
synchronized (session) {
// 发送消息
session.getBasicRemote().sendText(content);
}
}
} catch (IOException ioException) {
log.info("发送消息失败:{}", ioException.getMessage());
ioException.printStackTrace();
}
}
/**
* 获取当前时间
* @return String 12:00:00
*/
private String localDateTimeToString() {
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss");
return dateTimeFormatter.format( LocalDateTime.now());
}
@OnError
public void onerror(Session session, Throwable throwable){
System.out.println();
log.error(throwable.getMessage(), throwable);
}
}
评论区