最近在用SpringBoot框架实现消息推送的功能,用到了websocket;记录一下踩过的坑,
实现了消息单发与群发。
1. maven配置
项目用的是1.8的jkd和8.0的Tomcat。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
<version>1.3.5.RELEASE</version>
</dependency>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
2. websocket类的使用
package com.websocket.util;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
import javax.websocket.EndpointConfig;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;
@ServerEndpoint(value = "/websocket/{id}")//接受websocket请求路径
@Component//注册到spring容器中
public class MyWebSocket {
// 静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。
private static int onlineCount = 0;
// concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话,可以使用Map来存放,其中Key可以为用户标识
private static ConcurrentHashMap<String, MyWebSocket> webSocketSet = new ConcurrentHashMap<>();
// 与某个客户端的连接会话,需要通过它来给客户端发送数据
private Session session;
//指定的sid,具有唯一性,暫定為用戶id
private String sid = "";
/**
* 连接建立成功调用的方法
*
* @param session
* 可选的参数。session为与某个客户端的连接会话,需要通过它来给客户端发送数据
*/
@OnOpen
public void onOpen(@PathParam("id") String id,Session session, EndpointConfig config) {
String httpSessionId = (String) config.getUserProperties().get("sessionId");
this.session = session;
this.sid=id;
webSocketSet.put(sid,this); //加入set中
addOnlineCount(); // 在线数加1
// sendAll("当前在线人数为" + getOnlineCount());
}
/**
* 连接关闭调用的方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this); // 从set中删除
subOnlineCount(); // 在线数减1
// sendAll("当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message
* 客户端发送过来的消息
* @param session
* 可选的参数
*/
@OnMessage
public void onMessage(String message, Session session) {
//TODO 收到客户端消息后调用的方法
}
/**
* 发生错误时调用
*
* @param session
* @param error
*/
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
/**
* 实现服务器主动推送
*/
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
/**
* 发给固定人
*
* @param message
* @param userid
*/
public void sendMessage(String message,String sendUserId) {
for (String key : webSocketSet.keySet()) {
try {
if(sendUserId.equals(key)){
webSocketSet.get(key).sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 群发
*
* @param message
*/
public void sendAll(String message) {
for (String key : webSocketSet.keySet()) {
try {
webSocketSet.get(key).sendMessage(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
MyWebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
MyWebSocket.onlineCount--;
}
}
3.这里面需要注意
将项目打成war包时,需要注释掉 “//使用内置Tomcat需要加上”下面的代码;如果是使用springboot自带的Tomcat时,需要放开,要不然就访问不了
package com..websocket.util;
import javax.servlet.http.HttpSession;
import javax.websocket.HandshakeResponse;
import javax.websocket.server.HandshakeRequest;
import javax.websocket.server.ServerEndpointConfig;
import org.apache.catalina.session.StandardSessionFacade;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnWebApplication
public class GetHttpSessionConfigurator extends ServerEndpointConfig.Configurator {
/**
* 修改握手信息
*/
@Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
StandardSessionFacade ssf = (StandardSessionFacade) request.getHttpSession();
if (ssf != null) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
// 关键操作
sec.getUserProperties().put("sessionId", httpSession.getId());
}
super.modifyHandshake(sec, request, response);
}
//使用内置Tomcat需要加上
/* @Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
*/
}