花费了将近一周的时间终于将webSocket 集成到了原来的springmvc+spring +mybatis 项目中了,一路心酸一路的泪水啊,网上的技术文档都没有解决我的问题,(很多文章都是你抄我的我抄你的)。
先来说一下我的环境:
windows 10, myeclipse 2014,orcale11 ,jdk 1.7.0.79 ,spring 4.3.8 springmvc 4.2.4.RELEASE
websocket jar
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
下面就记录一下自己遇到的问题,以及如何解决的问题
想贴上自己的代码
SpringWebSocketConfig.java 文件
/**
*
*
* @Description: TODO <p>SpringWebSocketConfig.java</p>
* @作者: 王彦宝
* @时间: 2018年10月24日下午3:52:57
* @version V1.0
* @see java.lang.Class
* @since JDK{jdk1.7}
*/
//@EnableWebMvc//测试这个注解可以不要
@Configuration
@EnableWebSocket
public class SpringWebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer{
@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(webSocketHandler(),"/webSocketIMServer.json").addInterceptors(myInterceptor()).setAllowedOrigins("*");
registry.addHandler(webSocketHandler(), "/sockjs/webSocketIMServer.json").withSockJS();
}
@Bean
public SpringWebSocketHandler webSocketHandler(){
return new SpringWebSocketHandler();
}
@Bean
public SpringWebSocketHandlerInterceptor myInterceptor(){
return new SpringWebSocketHandlerInterceptor();
}
}
SpringWebSocketHandler.java 文件
import java.io.IOException;
import java.util.ArrayList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
/**
*
*
* @Description: TODO <p>SpringWebSocketHandler.java</p>
* @作者: 王彦宝
* @时间: 2018年10月24日下午3:55:49
* @version V1.0
* @see java.lang.Class
* @since JDK{jdk1.7}
*/
public class SpringWebSocketHandler extends TextWebSocketHandler{
private static final Logger LOGGER = LoggerFactory.getLogger(SpringWebSocketHandler.class);
private static final ArrayList<WebSocketSession> users;//这个会出现性能问题,最好用Map来存储,key用userid
static {
users = new ArrayList<WebSocketSession>();
}
public SpringWebSocketHandler() {
// TODO Auto-generated constructor stub
}
/**
* 连接成功时候,会触发页面上onopen方法
*/
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
LOGGER.info("connect to the websocket success......当前数量:"+users.size());
users.add(session);
//这块会实现自己业务,比如,当用户登录后,会把离线消息推送给用户,这里需要查询数据库把待发送的消息发送给医生
TextMessage returnMessage = new TextMessage("你将收到的离线");
session.sendMessage(returnMessage);
}
/**
* 关闭连接时触发
*/
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
String username= (String) session.getAttributes().get("WEBSOCKET_EDU_SESSION_USERID");
LOGGER.info("用户"+username+"已退出!");
users.remove(session);
LOGGER.info("剩余在线用户"+users.size());
}
/**
* 给某个用户发送消息
* @param userAndHosId
* @param message
* @作者: 王彦宝
* @时间: 2018年11月30日下午1:51:32
* @返回 void
*/
public void sendMessageToUser(String username, TextMessage message) {
LOGGER.info("发送消息:"+message);
for (WebSocketSession user : users) {
if (user.getAttributes().get("WEBSOCKET_SESSION_USERID").equals(username)) {
try {
if (user.isOpen()) {
LOGGER.info("发送消息:"+message);
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
break;
}
}
}
/**
* 群发所有在线用户消息
*
* @param userName
* @param message
*/
public void sendMessage(TextMessage message) {
LOGGER.info("发送消息:"+message);
for (WebSocketSession user : users) {
try {
if (user.isOpen()) {
LOGGER.info("发送消息:"+message);
user.sendMessage(message);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* js调用websocket.send时候,会调用该方法
*/
@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
super.handleTextMessage(session, message);
/**
* 收到消息,自定义处理机制,实现业务
*/
System.out.println("服务器收到消息:"+message);
if(message.getPayload().startsWith("#anyone#")){ //单发某人
sendMessageToUser((String)session.getAttributes().get("WEBSOCKET_SESSION_USERID"), new TextMessage("服务器单发:" +message.getPayload())) ;
}else if(message.getPayload().startsWith("#everyone#")){
sendMessage(new TextMessage("服务器群发:" +message.getPayload()));
}else{
}
}
}
SpringWebSocketHandlerInterceptor.java 文件
import java.util.Map;
import javax.servlet.http.HttpSession;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;
/**
*
* WebSocket拦截器
* @Description: TODO <p>SpringWebSocketHandlerInterceptor.java</p>
* @作者: 王彦宝
* @时间: 2018年10月24日下午3:58:34
* @version V1.0
* @see java.lang.Class
* @since JDK{jdk1.7}
*/
public class SpringWebSocketHandlerInterceptor extends HttpSessionHandshakeInterceptor{
@Override
public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Map<String, Object> attributes) throws Exception {
// TODO Auto-generated method stub
System.out.println("Before Handshake");
if (request instanceof ServletServerHttpRequest) {
ServletServerHttpRequest servletRequest = (ServletServerHttpRequest) request;
HttpSession session = servletRequest.getServletRequest().getSession(false);
if (session != null) {
//使用userName区分WebSocketHandler,以便定向发送消息
String username = (String) session.getAttribute("SESSION_USERID");
if (username==null) {
username="default-system";
}
attributes.put("WEBSOCKET_SESSION_USERID",username);
}
}
return super.beforeHandshake(request, response, wsHandler, attributes);
}
@Override
public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler,
Exception ex) {
// TODO Auto-generated method stub
super.afterHandshake(request, response, wsHandler, ex);
}
}
这就是核心的代码了。在spring 的配置文件中加入 SpringWebSocketConfig 文件的扫描路径,然后启动项目在前端进行访问
ws://localhost:8081/XXXXX/webSocketIMServer.json"); 这样访问就OK 了。
遇到的问题我总结一下:
1、404问题:
在第一次访问的时候报出了404 的错误,找不到路径,最后发现问题在这里
registry.addHandler(webSocketHandler(),"/webSocketIMServer.json").addInterceptors(myInterceptor()).setAllowedOrigins("*");
registry.addHandler(webSocketHandler(), "/sockjs/webSocketIMServer.json").withSockJS();
前端访问的方法必须和后台写的这个方法名一致(包括后缀名,如果后台代码没有后缀,前端访问时候就不要加后缀,不然就是访问不到)"/webSocketIMServer.json"。
2、403问题:
上面解决了404 的问题就又报出了403 的问题,然后我就在网上各种搜索,有各种各样的方案,都试过了,没有解决问题,然后搜到了这个
博客:https://blog.csdn.net/goxidono/article/details/53414897
和我的问题一模一样,说的挺详细,spring-webSocket.jar 里面自动实现了HandshakeInterceptor 接口
public class OriginHandshakeInterceptor implements HandshakeInterceptor {
然后再执行到这个方法的时候,this.interceptors 就会又两个 interceptors, 第一个是我们自己实现的SpringWebSocketHandlerInterceptor 这个接口实现类,第二个就是 spring-webSocket 自动帮你实现的 OriginHandshakeInterceptor 这个接口实现类,
public boolean applyBeforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
Map<String, Object> attributes) throws Exception {
for (int i = 0; i < this.interceptors.size(); i++) {
HandshakeInterceptor interceptor = this.interceptors.get(i);
if (!interceptor.beforeHandshake(request, response, this.wsHandler, attributes)) {
if (logger.isDebugEnabled()) {
logger.debug(interceptor + " returns false from beforeHandshake - precluding handshake");
}
applyAfterHandshake(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
就在这个方法中会被拦截,然后我就改变了写法,
下面是我刚开始的写法:
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//WebIM WebSocket通道
registry.addHandler(webSocketHandler(),"/webSocketIMServer").addInterceptors(myInterceptor());
registry.addHandler(webSocketHandler(), "/sockjs/webSocketIMServer").addInterceptors(myInterceptor()).withSockJS();
}
改变后的写法:
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
//WebIM WebSocket通道
registry.addHandler(webSocketHandler(),"/webSocketIMServer.json").addInterceptors(myInterceptor()).setAllowedOrigins("*");
registry.addHandler(webSocketHandler(), "/sockjs/webSocketIMServer.json").withSockJS();
}
然后继续测试,发现还是不行,原因在执行到 applyBeforeHandshake 方法时有两个拦截器,按照上面的写法我们自己实现的拦截器时通过了,但是spring-webSocket 实现的拦截器没有通过,然后又被拦截了报403 ,但是还是没有解决我的问题。文章里用的jdk 8 ,tomcat 8 ,和我的问题相似但是环境不同,用不了他的写法,测试到这里的时候我用的spirng 的版本 时4.2.4 ,然后我就想想换一个高点的spring 的版本试试看,之后就换了Spirng 4.3.8 的版本,启动测试,居然OK了。好吧原来时版本的问题 最后的的环境是:
windows 10, myeclipse 2014,orcale11 ,jdk 1.7.0.79 ,spring 4.3.8 springmvc 4.2.4.RELEASE
websocket jar
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>4.2.4.RELEASE</version>
</dependency>
这样就可以了。在此做一个记录。