前言

现在主流的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 使用笔记相关推荐

  1. Spring Boot学习笔记-基础(2)

    Spring Boot学习笔记-基础(2) Spring Boot 优点: – 快速创建独立运行的Spring项目以及与主流框架集成 – 使用嵌入式的Servlet容器,应用无需打成WAR包 – st ...

  2. Spring Cloud 学习笔记(2 / 3)

    Spring Cloud 学习笔记(1 / 3) Spring Cloud 学习笔记(3 / 3) - - - 56_Hystrix之全局服务降级DefaultProperties 57_Hystri ...

  3. Spring Cloud 学习笔记(2 3)

    Spring Cloud 学习笔记(1 / 3) Spring Cloud 学习笔记(3 / 3) - - - 56_Hystrix之全局服务降级DefaultProperties 57_Hystri ...

  4. 超赞:不愧是阿里内部“Spring boot学习笔记”从头到尾,全是精华

    spring boot为何会出现? 随着动态语言的流行(Ruby.Groovy. Scala. Node.js),Java 的开发显得格外的笨重:繁多的配置.低下的开发效率.复杂的部署流程以及第三方技 ...

  5. Spring MVC 学习笔记 对locale和theme的支持

    Spring MVC 学习笔记 对locale和theme的支持 Locale Spring MVC缺省使用AcceptHeaderLocaleResolver来根据request header中的 ...

  6. springaop事务逻辑原理_太狠了!阿里大牛手写的Spring核心面试笔记:IOC+AOP+MVC+事务...

    Spring作为现在最流行的java 开发技术,其内部源码设计非常优秀.如果你不会Spring,那么很可能面试官会让你回家等通知. Spring是什么? 有一个工地,几百号人在用铁锹铲子挖坑. 如果开 ...

  7. Spring Cloud 学习笔记(四)-Spring Cloud Hystrix

    Spring Cloud 学习笔记(四)-Spring Cloud Hystrix 由于前一阵子项目的原因,今天才继续弄上,今天想学习一下Hystrix组件 这个组件还挺抽象的,最开始我一直没太明白, ...

  8. spring websocket源码分析续Handler的使用

    1. handler的定义 spring websocket支持的消息有以下几种: 对消息的处理就使用了Handler模式,抽象handler类AbstractWebSocketHandler.jav ...

  9. Spring Boot学习笔记-实践建言

    2019独角兽企业重金招聘Python工程师标准>>> 本文延续<Spring Boot学习笔记-快速示例>,从开发指南中摘出一些实践经验可供参考.这也是笔者看到的眼前一 ...

最新文章

  1. python multiprocessing manager list error: [Errno 2] No such file or directory
  2. nginx实现防止ddos攻击
  3. 笔记-中项案例题-2017年上-计算题
  4. 初中数学四十二个几何模型_模型 | 一文搞定初中数学9大重要几何模型(优选)...
  5. mysql5.718解压版安装_MySQL v5.7.18 版本解压安装
  6. Learning to rank的讲解,单文档方法(Pointwise),文档对方法(Pairwise),文档列表方法(Listwise)
  7. P(A)P(B|A)=P(B)P(A|B)
  8. Too many open files 问题的解决
  9. js 将16进制颜色转为RGBA
  10. 目标管理 - SMART原则
  11. java数据结构 mobi_数据结构:Java语言描述(第2版) pdf epub mobi txt 下载
  12. CSS内联样式表、内部样式表、外部样式表
  13. 垃圾邮件过滤技术发展现状及展望
  14. 为何台湾在移动互联网时代远远落后于大陆?
  15. 字节跳动实习生转正工资_字节跳动西瓜视频招聘 | 新媒体运营实习生
  16. BMZCTF:insomniteaser_2019_l33t_hoster
  17. amigo幸运字符什么意思_QQ有什么魅力?为什么00后都喜欢?细节都在这些“标识”里...
  18. I came, I saw, I hacked Automated Generation of Process-independent Attacks for ICS
  19. 自己diy的一个简单的家居管理系统
  20. 【剑桥摄影协会】针对摄影的显示器校准

热门文章

  1. HUST Trainning 2015-06-14
  2. Ant Design 台湾TW地区二级联动 数据
  3. 刻意练习这本书。。。。。。。
  4. matlab解方程组解析解
  5. allergro音乐术语什么意思_常见音乐术语(速度术语)
  6. 计算机二级考风考纪主题班会,2021年我国计算机二级考试基础概述.doc
  7. 手机拍摄证件照可以用什么软件
  8. 啤酒与尿布的蝴蝶效应——关联分析
  9. 京东云php环境配置,干货 | 京东云应用负载均衡(ALB)多功能实操
  10. MATLAB 制作gif动态图