1.加入依赖
2.后端建立socket服务端
3.前端建立客户端
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-websocketartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.76version>
dependency>
// 配置开启WebSocket
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
/**
* @author websocket服务
*/
@ServerEndpoint(value = "/imserver/{userId}")
@Component
public class WebSocketServer {
private static UserService userService;
private static RedisTemplate redisTemplate;
public static void setUserService(ApplicationContext context){
userService = context.getBean(UserServiceImpl.class);
redisTemplate = (RedisTemplate) context.getBean("redisTemplate");
}
private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class);
/**
* 记录当前在线连接数
*/
public static final Map<String, Session> sessionMap = new ConcurrentHashMap<>();
/**
* 连接建立成功调用的方法
*/
// 当前用户
private UserVo userVo;
// 连接上服务端触发的方法
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) {
if (StringUtils.hasText(userId)){
// 加入新用户
if (sessionMap.containsKey(userId)){
sessionMap.remove(userId);
}
sessionMap.put(userId, session);
this.userVo = userService.findById(Long.valueOf(userId));
// 统计所有在线用户
List<UserVo> list = new LinkedList<>();
sessionMap.forEach((userId1,session1) -> {
UserVo userVo = userService.findById(Long.valueOf(userId1));
list.add(userVo);
});
try {
// 发送给所有在线的用户,更新在线人数
sendAllMessage(JSON.toJSONString(list));
} catch (Exception e) {
e.printStackTrace();
}
log.info("有新用户加入,userId={}, 当前在线人数为:{}", userId, sessionMap.size());
}
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose(Session session, @PathParam("userId") String userId) {
sessionMap.remove(userId);
// 统计所有在线用户
List<UserVo> list = new LinkedList<>();
sessionMap.forEach((userId1,session1) -> {
UserVo userVo = userService.findById(Long.valueOf(userId1));
list.add(userVo);
});
sendAllMessage(JSON.toJSONString(list));
log.info("有一连接关闭,移除userId={}的用户session, 当前在线人数为:{}", userId, sessionMap.size());
}
/**
* 收到客户端消息后调用的方法
* 后台收到客户端发送过来的消息
* onMessage 是一个消息的中转站
* 接受 浏览器端 socket.send 发送过来的 json数据
* @param message 客户端发送过来的消息
*/
@OnMessage
public void onMessage(String message, Session session, @PathParam("userId") String userId) {
userVo = userService.findById(Long.valueOf(userId));
log.info("服务端收到用户username={},id={}的消息:{}", userVo.getNickname(),userId, message);
// 解析消息
JSONObject jsonObject1 = JSON.parseObject(message);
String toUserId = jsonObject1.getString("toUserId");
String text = jsonObject1.getString("text");
// 判断是给指定人发,还是群发
if (StringUtils.hasText(toUserId)){
// {"to": "admin", "text": "聊天文本"}
Session toSession = sessionMap.get(toUserId); // 根据 to用户名来获取 session,再通过session发送消息文本
if (toSession != null) {
// 服务器端 再把消息组装一下,组装后的消息包含发送人和发送的文本内容
// {"from": "zhang", "text": "hello"}
JSONObject jsonObject = new JSONObject();
jsonObject.put("fromUser",userVo);
jsonObject.put("toUser",userService.findById(Long.valueOf(toUserId)));
jsonObject.put("text",text);
this.sendMessage(jsonObject.toJSONString(), toSession);
log.info("发送给用户userId={},消息:{}", toUserId, jsonObject.toJSONString());
} else {
log.info("发送失败,未找到用户username={}的session", toUserId);
}
}else{
JSONObject jsonObject = new JSONObject();
jsonObject.put("fromUser",userVo);
jsonObject.put("text",text);
this.sendAllMessage(jsonObject.toJSONString());
// 将消息存入redis
redisTemplate.opsForList().rightPush("messageList",jsonObject.toJSONString());
redisTemplate.expire("messageList",60*60, TimeUnit.SECONDS); // 过期时间
log.info("发送给所有用户,消息:{}", toUserId, jsonObject.toJSONString());
}
}
@OnError
public void onError(Session session, Throwable error) {
log.error("发生错误");
error.printStackTrace();
}
/**
* 服务端发送消息给客户端
*/
private void sendMessage(String message, Session toSession) {
try {
log.info("服务端给客户端[{}]发送消息{}", toSession.getId(), message);
toSession.getBasicRemote().sendText(message);
} catch (Exception e) {
log.error("服务端发送消息给客户端失败", e);
}
}
/**
* 服务端发送消息给所有客户端
*/
private void sendAllMessage(String message) {
try {
for (Session session : sessionMap.values()) {
log.info("服务端给客户端[{}]发送消息{}", session.getId(), message);
session.getBasicRemote().sendText(message);
}
} catch (Exception e) {
log.error("服务端发送消息给客户端失败", e);
}
}
}
// WebSocket服务类无法进行bean的注入,所以要自己调用ApplicationContext获取bean再注入
@SpringBootApplication
public class BlogApplication {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = SpringApplication.run(BlogApplication.class, args);
WebSocketServer.setUserService(applicationContext);
}
}
在线用户
{{user.account}}
当前用户({{user.account}})
发送
springboot集成websocket实现聊天室的功能。如有不足之处,还望大家斧正。