-
添加配置类
需要添加 HttpSessionConfig 和 WebSocketConfig 两个配置类
HttpSessionConfig
import jakarta.servlet.http.HttpSession;
import jakarta.websocket.HandshakeResponse;
import jakarta.websocket.server.HandshakeRequest;
import jakarta.websocket.server.ServerEndpointConfig;
/**
* @Description: 用于获取和保存HttpSession对象
* @Author: 翰戈.summer
* @Date: 2023/11/16
* @Param:
* @Return:
*/
public class HttpSessionConfig extends ServerEndpointConfig.Configurator {
@Override
public void modifyHandshake(ServerEndpointConfig serverEndpointConfig, HandshakeRequest handshakeRequest, HandshakeResponse handshakeResponse) {
//获取HttpSession对象
HttpSession httpSession = (HttpSession) handshakeRequest.getHttpSession();
//保存HttpSession对象
serverEndpointConfig.getUserProperties().put(HttpSession.class.getName(), httpSession);
}
}
WebSocketConfig
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
/**
* @Description: 扫描添加@ServerEndpoint注解的Bean
* @Author: 翰戈.summer
* @Date: 2023/11/16
* @Param:
* @Return:
*/
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
-
在线聊天
封装消息类 ChatMessageDTO 和 ChatMessageVO
ChatMessageDTO
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description: 封装服务器接收消息对象
* @Author: 翰戈.summer
* @Date: 2023/11/20
* @Param:
* @Return:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ChatMessageDTO {
//接收消息用户的用户名
private String toUsername;
//消息内容
private String content;
}
ChatMessageVO
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description: 封装服务器发出消息对象
* @Author: 翰戈.summer
* @Date: 2023/11/20
* @Param:
* @Return:
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ChatMessageVO {
//是否为系统消息
private Boolean isSystemMessage;
//发出消息用户的用户名
private String fromUsername;
//消息内容
private String content;
}
MessageUtils 工具类
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Component;
/**
* @Description: 封装服务器发出消息对象,并转成json格式的字符串
* @Author: 翰戈.summer
* @Date: 2023/11/20
* @Param:
* @Return:
*/
@Component
public class MessageUtils {
/**
* @Description: 用于服务器向用户发出消息
* @Author: 翰戈.summer
* @Date: 2023/11/20
* @Param: Boolean, String, String
* @Return: String
*/
public static String getJsonString(Boolean isSystemMessage, String fromUsername, String content) {
ChatMessageVO chatMessageVO = new ChatMessageVO(isSystemMessage, fromUsername, content);
return JSONObject.toJSONString(chatMessageVO);
}
}
在线聊天代码实现
import com.alibaba.fastjson.JSONObject;
import jakarta.servlet.http.HttpSession;
import jakarta.websocket.*;
import jakarta.websocket.server.ServerEndpoint;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Description: WebSocket实现在线聊天
* @Author: 翰戈.summer
* @Date: 2023/11/17
* @Param:
* @Return:
*/
@Slf4j
@Component
@ServerEndpoint(value = "/chat", configurator = HttpSessionConfig.class)
public class WebSocketServer {
private static final Map<String, Session> onlineUsers = new ConcurrentHashMap<>();
private HttpSession httpSession;
/**
* @Description: 在建立websocket连接时被调用
* @Author: 翰戈.summer
* @Date: 2023/11/17
* @Param: Session, EndpointConfig
* @Return: void
*/
@OnOpen
public void onOpen(Session session, EndpointConfig endpointConfig) {
//保存用户Session对象
this.httpSession = (HttpSession) endpointConfig.getUserProperties().get(HttpSession.class.getName());
String username = (String) this.httpSession.getAttribute("username");
onlineUsers.put(username, session);
//向所有在线用户广播
String usernameSetString = getAllOnlineUsername();
String message = MessageUtils.getJsonString(true, null, usernameSetString);
broadcastToAllOnlineUsers(message);
}
/**
* @Description: 在接收消息时被调用,将消息发送到指定用户
* @Author: 翰戈.summer
* @Date: 2023/11/17
* @Param: String
* @Return: void
*/
@OnMessage
public void onMessage(String message) throws IOException {
ChatMessageDTO chatMessageDTO = JSONObject.parseObject(message, ChatMessageDTO.class);
//获取消息接收用户的用户名
String toUsername = chatMessageDTO.getToUsername();
//获取消息内容
String content = chatMessageDTO.getContent();
//获取消息接收用户Session对象
Session session = onlineUsers.get(toUsername);
//获取消息发出用户的用户名
String username = (String) this.httpSession.getAttribute("username");
String msg = MessageUtils.getJsonString(false, username, content);
session.getBasicRemote().sendText(msg);
}
/**
* @Description: 在断开websocket连接时被调用
* @Author: 翰戈.summer
* @Date: 2023/11/17
* @Param: Session
* @Return: void
*/
@OnClose
public void onClose() throws IOException {
//去除用户Session对象
String username = (String) this.httpSession.getAttribute("username");
try (Session ignored = onlineUsers.remove(username)) {
//向所有在线用户广播
String usernameSetString = getAllOnlineUsername();
String message = MessageUtils.getJsonString(true, null, usernameSetString);
broadcastToAllOnlineUsers(message);
}
}
/**
* @Description: 向所有在线用户广播
* @Author: 翰戈.summer
* @Date: 2023/11/17
* @Param: String
* @Return: void
*/
private void broadcastToAllOnlineUsers(String message) {
Collection<Session> sessions = onlineUsers.values();
sessions.forEach(session -> {
try {
session.getBasicRemote().sendText(message);
} catch (Exception e) {
throw new RuntimeException("消息异常!");
}
}
);
}
/**
* @Description: 获取所有在线用户的用户名
* @Author: 翰戈.summer
* @Date: 2023/11/20
* @Param:
* @Return: String
*/
private String getAllOnlineUsername() {
Set<String> usernameSet = onlineUsers.keySet();
return usernameSet.toString();
}
}