最近项目中需要完成信息的实时显示功能,因此考虑使用websocket来实现,废话不多说,直接上配置流程及代码。
1. 首先引入需要的jar包,spring和springmvc的自然不多说,主要是引入spring集成的websocket包
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>${spring.version}</version>
</dependency>
2. 编写websocket配置类,当然也可以在xml配置(这里就不多说了)
编写自己的WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer
编写这个类主要是为了实现WebSocketConfigurer.registerWebSocketHandlers方法给websocket注册消息处理器WebSocketHandler。具体实现如下:
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements
WebSocketConfigurer {
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 前台 可以使用websocket环境
registry.addHandler(webSocketHandler(), "/websocket").addInterceptors(new HandshakeInterceptor());
// 前台 不可以使用websocket环境,则使用sockjs进行模拟连接
registry.addHandler(webSocketHandler(),"/sockjs/websocket").addInterceptors(
new HandshakeInterceptor()).withSockJS();
}
// websocket 处理类@Bean
public WebSocketHandler webSocketHandler() {
return new MySocketHandler();
}
}
3. 编写websocket消息处理器MySocketHandler implements WebSocketHandler
这个类是为了处理前端发送的消息或推送消息给前端,也对所有的连接进行管理
public class MyWebSocketHandler implements WebSocketHandler {
private static final Logger log = Logger.getLogger(ICWantWebSocketHandler.class);
@Autowired
RedisUtils redisUtils;
// 保存所有的用户session
private static final Map<String, WebSocketSession> users = new HashMap<>();
// 连接 就绪时
@Override
public void afterConnectionEstablished(WebSocketSession session)
throws Exception {
log.info("connect websocket success.......");
//这块会实现自己业务,比如,当用户登录后,会把离线消息推送给用户
//TextMessage returnMessage = new TextMessage("你将收到的离线");
//session.sendMessage(returnMessage);
String username= (String) session.getAttributes().get("WEBSOCKET_USERNAME");
System.out.println(username);
System.out.println(session.getId());
//判断是否有同名连接,若存在则关闭
WebSocketSession s = users.get(username);
if(null != s && s.isOpen())
s.close();
users.put(username, session);
System.out.println("connect to the websocket success......当前数量:"+users.size());
}
// 处理前端发送的消息信息
@Override
public void handleMessage(WebSocketSession session,
WebSocketMessage<?> message) throws Exception {
//获取消息体
String msg = message.getPayload().toString();
if(msg.startsWith("java.nio.HeapByteBuffer")){
System.out.println("浏览器发送的心跳包");
return;
}
}
// 处理传输时异常
@Override
public void handleTransportError(WebSocketSession session,
Throwable exception) throws Exception {
//log.info("数据传输异常: " + exception.getMessage());
}
// 关闭 连接时
@Override
public void afterConnectionClosed(WebSocketSession session,
CloseStatus closeStatus) throws Exception {
log.info("connect websocket closed.......");
String username= (String) session.getAttributes().get("WEBSOCKET_USERNAME");
System.out.println("用户"+username+"已退出!");
users.remove(username);
System.out.println("剩余在线用户" + users.size());
}
@Override
public boolean supportsPartialMessages() {
return false;
}
// 给所有用户发送 信息
public void sendMsgToAllUsers(SocketMessageVO message) {
for (WebSocketSession user : users.values()) {
String msg = GsonUtil.object2JsonStr(message);
TextMessage textMsg = new TextMessage(msg);
try {
user.sendMessage(textMsg);
} catch (IOException e) {
log.error("XLTX-ERROR: websocket发送消息出错", e);
}
}
}
/**
* 发送消息给指定的用户
* @param SocketMessageVO from_user 或 to_user为null时直接返回
*/
public void sendMsgToUser(SocketMessageVO message) {
if(message == null || message.getFrom_user() == null || message.getTo_user() == null)
return;
String toUser = message.getTo_user();
WebSocketSession session = users.get(toUser);
if(session == null)
return;
String msg = GsonUtil.object2JsonStr(message);
TextMessage textMsg = new TextMessage(msg);
try {
session.sendMessage(textMsg);
} catch (IOException e) {
log.error("XLTX-ERROR: websocket发送消息出错", e);
}
}
}
4. 编写握手拦截器 HandshakeInterceptor, 主要是为了在连接前将当前用户的信息保存起来,方便服务器推送信息
public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor {
// 初次握手访问前
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler webSocketHandler, Map<String, Object> attributes)
throws Exception {
if (request instanceof ServletServerHttpRequest) {
Session httpSession = SecurityUtils.getSubject().getSession();
if(httpSession != null){
//使用userName区分WebSocketHandler,以便定向发送消息, 如果需要向指定的用户发送消息则需要记录用户信息
//因此这里将用户id绑定到websocket的session
UserVO userVO = (UserVO)httpSession.getAttribute(SysConstant.SESSION_USER_INFO);
if(userVO != null){
long uid = userVO.getId();
String userName = String.valueOf(uid);
if (userName==null) {
userName="default-system";
}
attributes.put("WEBSOCKET_USERNAME", userName);
}else
attributes.put("WEBSOCKET_USERNAME", httpSession.getId());
}
}
return super.beforeHandshake(request, response, webSocketHandler, attributes);
}
// 初次握手访问后
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,
WebSocketHandler wsHandler, Exception e) {
super.afterHandshake(request, response, wsHandler, e);
}
}
以上个步骤就完成了websocket的服务端配置,在需要推送信息的类中注入消息处理器,即可进行消息推送。
前端的编写可以参考:https://blog.csdn.net/jared_he2017/article/details/79886600