Spring Websocket 使用笔记
前言
现在主流的web容器基本均已支持websocket,但各容器的websocket接口都不尽相同。为了统一websocket实现,便于今后在不同web容器间的移植,这里使用spring websocket框架集成。
spring依赖
添加spring所需依赖
<spring.framework.version>4.2.1.RELEASE</spring.framework.version><dependency><!-- spring-core --><groupId>org.springframework</groupId><artifactId>spring-core</artifactId><version>${spring.framework.version}</version></dependency><dependency><!-- spring-beans --><groupId>org.springframework</groupId><artifactId>spring-beans</artifactId><version>${spring.framework.version}</version></dependency><dependency><!-- spring-context --><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.framework.version}</version></dependency><dependency><!-- spring-context-support --><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>${spring.framework.version}</version></dependency><dependency><!-- spring-aop --><groupId>org.springframework</groupId><artifactId>spring-aop</artifactId><version>${spring.framework.version}</version></dependency><dependency><!-- spring-expression --><groupId>org.springframework</groupId><artifactId>spring-expression</artifactId><version>${spring.framework.version}</version></dependency><dependency><!-- spring-aspects --><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>${spring.framework.version}</version></dependency><dependency><!-- spring-web --><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>${spring.framework.version}</version></dependency><dependency><!-- spring-websocket --><groupId>org.springframework</groupId><artifactId>spring-websocket</artifactId><version>${spring.framework.version}</version></dependency>
websocket依赖
对于jetty,添加如下依赖
<dependency><groupId>org.eclipse.jetty.websocket</groupId><artifactId>websocket-server</artifactId><version>9.3.3.v20150827</version><scope>provided</scope></dependency>
对于tomcat,添加如下依赖
<dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-websocket</artifactId><version>8.0.23</version><scope>provided</scope></dependency>
对于上述jetty或者tomcat依赖,需要注意依赖版本与容器版本的兼容性,另一方面需要注意将依赖的scope设置为provided
添加Handler
添加HandshakeInterceptor
/*** <pre>* 豆瓣电台websocket握手接口实现* </pre>**@author ManerFan 2015年9月7日*/
@Component
public class DoubanFMHandshakeInterceptor extends HttpSessionHandshakeInterceptor {@Overridepublic boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {// 握手前的处理逻辑return super.beforeHandshake(request, response, wsHandler, attributes);}@Overridepublic void afterHandshake(ServerHttpRequest request, ServerHttpResponse response,WebSocketHandler wsHandler, Exception ex) {// 握手后的处理逻辑super.afterHandshake(request, response, wsHandler, ex);}}
spring提供了多种HandshakeInterceptor实现,可以根据实际需要在其基础上实现自己的逻辑。当然,也可以直接使用spring的默认HandshakeInterceptor
添加WebSocketHandler
/*** <pre>* 豆瓣FM websocket消息处理* </pre>**@author ManerFan 2015年9月7日*/
@Component
public class DoubanFMWebSocketHandler extends TextWebSocketHandler {/*** logger*/private static final Logger LOGGER = CommonLogger.getLogger();/*** 记录连接上的所有session*/private Map<String, WebSocketSession> sessionMap = Collections.synchronizedMap(new HashMap<String, WebSocketSession>());@Overridepublic void afterConnectionEstablished(WebSocketSession session) throws Exception {// 建立连接之后LOGGER.info("[{}:{}] has bean connected", session.getUri(), session.getId());sessionMap.put(session.getId(), session);}@Overrideprotected void handleTextMessage(WebSocketSession session, TextMessage message)throws Exception {// 收到消息之后String payload = message.getPayload();Signalling signalling = JacksonUtils.instance().readValue(payload, TYPE_REFERENCE);if (null == signalling) {LOGGER.warn("Receive an Empty Payload [?][{}] from [{}:{}].", payload, session.getUri(),session.getId());return;}switch (signalling.getSignal()) {case C_START: /* 播放某一频道 */doStart(session, signalling.getMessage());break;case C_STOP: /* 停止收听 */doStop();break;case C_SKIP: /* 跳过该首 */doSkip(signalling.getMessage());break;case C_RATE: /* 喜欢 */doRate(signalling.getMessage());break;case C_UNRATE: /* 取消喜欢 */doUnRate(signalling.getMessage());break;case C_BYE: /* 不再收听 */doBye(signalling.getMessage());break;default:break;}}@Overridepublic void afterConnectionClosed(WebSocketSession session, CloseStatus status)throws Exception {// 连接断开之后LOGGER.info("[{}:{}] has bean closed", session.getUri(), session.getId());sessionMap.remove(session.getId());if (sessionMap.isEmpty()) {/* 没有客户端在线了 */doStop();}}
}
spring同样提供了TextWebSocketHandler BinaryWebSocketHandler等多种WebSocketHandler,可根据实际需要进行选择
初始化websocket
websocket的初始化,可以使用配置方式,也可以使用注解方式,关于spring websocket的使用及配置,详见websocket-doc,这里记录注解方式初始化方法
/*** <pre>* WebSocket初始化配置* * <a href="http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html">DOC for Spring Websocket</a>* </pre>**@author ManerFan 2015年9月7日*/
@Configuration
@EnableWebMvc
@EnableWebSocket
public class WebSocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {/*** <beans xmlns="http://www.springframework.org/schema/beans" * xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" * xmlns:websocket="http://www.springframework.org/schema/websocket" * xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd * http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd">* * <bean id="handler" class="com.manerfan.webapp.controller.douban.fm.websockt.FMWebSocketHandler"/> ** <websocket:handlers> * <websocket:mapping path="/websocket/doubanfm" handler="handler"/> * <websocket:handshake-interceptors> * <bean class="com.manerfan.webapp.controller.douban.fm.websock.FMHandshakeInterceptor"/> * </websocket:handshake-interceptors> * <!-- <websocket:sockjs/> --> * </websocket:handlers>* </beans>*//** Douban FM */@Autowiredprivate DoubanFMWebSocketHandler doubanFmWebSocketHandler;@Autowiredprivate DoubanFMHandshakeInterceptor doubanFMHandshakeInterceptor;@Overridepublic void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {/** Douban FM */registry.addHandler(doubanFmWebSocketHandler, "/websocket/doubanfm").addInterceptors(doubanFMHandshakeInterceptor).setAllowedOrigins("*")/*不加AllowedOrigins,有可能会全拒绝*/;/*registry.addHandler(doubanFmWebSocketHandler, "/sockjs/doubanfm").addInterceptors(doubanFMHandshakeInterceptor).withSockJS();*/ /*用于支持SockJS*/}/*** Each underlying WebSocket engine exposes configuration properties * that control runtime characteristics such as the size of message buffer sizes, * idle timeout, and others.*//*** For Tomcat, WildFly, and GlassFish add a ServletServerContainerFactoryBean* to your WebSocket Java config:*//*@Beanpublic ServletServerContainerFactoryBean createWebSocketContainer() {ServletServerContainerFactoryBean container = new ServletServerContainerFactoryBean();container.setMaxTextMessageBufferSize(8192);container.setMaxBinaryMessageBufferSize(8192);return container;}*//*** For Jetty, you’ll need to supply a pre-configured Jetty WebSocketServerFactory* and plug that into Spring’s DefaultHandshakeHandler through your WebSocket Java config:*/@Beanpublic DefaultHandshakeHandler handshakeHandler() {WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);policy.setInputBufferSize(8192); /* 设置消息缓冲大小 */policy.setIdleTimeout(600000); /* 10分钟read不到数据的话,则断开该客户端 */return new DefaultHandshakeHandler(new JettyRequestUpgradeStrategy(new WebSocketServerFactory(policy)));}}
setAllowedOrigins用于约束允许建立连接的客户端
withSockJS用于提供SockJS支持
至此,websocket后台框架便搭建完成
javascript可使用如下方式与后台建立连接进行通信
/* 建立连接 */
var ws = new WebSocket("ws://localhost/rasp/websocket/doubanfm");
ws.onopen = function() { /* 连接成功时 */ws.send(JSON.stringify({signal:'C_START', message:'179'}));
};
ws.onmessage = function (evt){ /* 收到消息时 */var received_msg = evt.data;console.info("Message is received... " + received_msg);
};
ws.onclose = function() { /* 连接断开时 */console.warn("Connection is closed...");
};
ws.onerror = function(e) { /* 出现错误时 */console.error(e);
}
开发时遇到的异常及解决办法
- No suitable default RequestUpgradeStrategy found
严重: Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘webSocketHandlerMapping’ defined in class path resource [org/springframework/web/socket/config/annotation/DelegatingWebSocketConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method ‘webSocketHandlerMapping’ threw exception; nested exception is java.lang.IllegalStateException: No suitable default RequestUpgradeStrategy found
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:599)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1111)
at …
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method ‘webSocketHandlerMapping’ threw exception; nested exception is java.lang.IllegalStateException: No suitable default RequestUpgradeStrategy found
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189)
at …
Caused by: java.lang.IllegalStateException: No suitable default RequestUpgradeStrategy found
at org.springframework.web.socket.server.support.DefaultHandshakeHandler.initRequestUpgradeStrategy(DefaultHandshakeHandler.java:127)
at …
出现上述异常,是由于缺少RequestUpgradeStrategy
对于jetty,需要添加如下依赖
<dependency><groupId>org.eclipse.jetty.websocket</groupId><artifactId>websocket-server</artifactId><version>9.3.3.v20150827</version><scope>provided</scope></dependency>
对于tomcat,需要添加如下依赖
<dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-websocket</artifactId><version>8.0.23</version><scope>provided</scope></dependency>
- java.lang.ClassCastException: org.eclipse.jetty.server.HttpConnection cannot be cast to org.eclipse.jetty.server.HttpConnection
页面报如下异常
WebSocket connection to ‘ws://localhost/rasp/websocket/doubanfm’ failed: Error during WebSocket handshake: Unexpected response code: 500
后台报如下异常
WARN:oejs.ServletHandler:qtp1237514926-20:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.socket.server.HandshakeFailureException: Uncaught failure for request http://localhost:80/rasp/websocket/doubanfm; nested exception is java.lang.ClassCastException: org.eclipse.jetty.server.HttpConnection cannot be cast to org.eclipse.jetty.server.HttpConnection
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:978)
at …
Caused by:
org.springframework.web.socket.server.HandshakeFailureException: Uncaught failure for request http://localhost:80/rasp/websocket/doubanfm; nested exception is java.lang.ClassCastException: org.eclipse.jetty.server.HttpConnection cannot be cast to org.eclipse.jetty.server.HttpConnection
at org.springframework.web.socket.server.support.WebSocketHttpRequestHandler.handleRequest(WebSocketHttpRequestHandler.java:135)
at …
Caused by:
java.lang.ClassCastException: org.eclipse.jetty.server.HttpConnection cannot be cast to org.eclipse.jetty.server.HttpConnection
at org.eclipse.jetty.websocket.server.WebSocketServerFactory.acceptWebSocket(WebSocketServerFactory.java:182)
at …
出现此情况,一般是由于工程中引用的javax.servlet与web容器相关jar包冲突导致
将javax.servlet相关依赖scope置为provided即可
<dependency><groupId>javax.servlet</groupId><artifactId>servlet-api</artifactId><scope>provided</scope><!-- 只在编译的时候 --></dependency>
- Unexpected response code: 403
页面报此异常,一般是由于websocket拒绝了此链接请求
在websocket初始化时,将setAllowedOrigins
加上即可
Spring Websocket 使用笔记相关推荐
- Spring Boot学习笔记-基础(2)
Spring Boot学习笔记-基础(2) Spring Boot 优点: – 快速创建独立运行的Spring项目以及与主流框架集成 – 使用嵌入式的Servlet容器,应用无需打成WAR包 – st ...
- Spring Cloud 学习笔记(2 / 3)
Spring Cloud 学习笔记(1 / 3) Spring Cloud 学习笔记(3 / 3) - - - 56_Hystrix之全局服务降级DefaultProperties 57_Hystri ...
- Spring Cloud 学习笔记(2 3)
Spring Cloud 学习笔记(1 / 3) Spring Cloud 学习笔记(3 / 3) - - - 56_Hystrix之全局服务降级DefaultProperties 57_Hystri ...
- 超赞:不愧是阿里内部“Spring boot学习笔记”从头到尾,全是精华
spring boot为何会出现? 随着动态语言的流行(Ruby.Groovy. Scala. Node.js),Java 的开发显得格外的笨重:繁多的配置.低下的开发效率.复杂的部署流程以及第三方技 ...
- Spring MVC 学习笔记 对locale和theme的支持
Spring MVC 学习笔记 对locale和theme的支持 Locale Spring MVC缺省使用AcceptHeaderLocaleResolver来根据request header中的 ...
- springaop事务逻辑原理_太狠了!阿里大牛手写的Spring核心面试笔记:IOC+AOP+MVC+事务...
Spring作为现在最流行的java 开发技术,其内部源码设计非常优秀.如果你不会Spring,那么很可能面试官会让你回家等通知. Spring是什么? 有一个工地,几百号人在用铁锹铲子挖坑. 如果开 ...
- Spring Cloud 学习笔记(四)-Spring Cloud Hystrix
Spring Cloud 学习笔记(四)-Spring Cloud Hystrix 由于前一阵子项目的原因,今天才继续弄上,今天想学习一下Hystrix组件 这个组件还挺抽象的,最开始我一直没太明白, ...
- spring websocket源码分析续Handler的使用
1. handler的定义 spring websocket支持的消息有以下几种: 对消息的处理就使用了Handler模式,抽象handler类AbstractWebSocketHandler.jav ...
- Spring Boot学习笔记-实践建言
2019独角兽企业重金招聘Python工程师标准>>> 本文延续<Spring Boot学习笔记-快速示例>,从开发指南中摘出一些实践经验可供参考.这也是笔者看到的眼前一 ...
最新文章
- python multiprocessing manager list error: [Errno 2] No such file or directory
- nginx实现防止ddos攻击
- 笔记-中项案例题-2017年上-计算题
- 初中数学四十二个几何模型_模型 | 一文搞定初中数学9大重要几何模型(优选)...
- mysql5.718解压版安装_MySQL v5.7.18 版本解压安装
- Learning to rank的讲解,单文档方法(Pointwise),文档对方法(Pairwise),文档列表方法(Listwise)
- P(A)P(B|A)=P(B)P(A|B)
- Too many open files 问题的解决
- js 将16进制颜色转为RGBA
- 目标管理 - SMART原则
- java数据结构 mobi_数据结构:Java语言描述(第2版) pdf epub mobi txt 下载
- CSS内联样式表、内部样式表、外部样式表
- 垃圾邮件过滤技术发展现状及展望
- 为何台湾在移动互联网时代远远落后于大陆?
- 字节跳动实习生转正工资_字节跳动西瓜视频招聘 | 新媒体运营实习生
- BMZCTF:insomniteaser_2019_l33t_hoster
- amigo幸运字符什么意思_QQ有什么魅力?为什么00后都喜欢?细节都在这些“标识”里...
- I came, I saw, I hacked Automated Generation of Process-independent Attacks for ICS
- 自己diy的一个简单的家居管理系统
- 【剑桥摄影协会】针对摄影的显示器校准