首页 > 学院 > 开发设计 > 正文

springMVC结合websocket小结

2019-11-06 06:02:10
字体:
来源:转载
供稿:网友

由于浏览器的历史遗留性问题,不是所有的浏览器都是支持WebSocket的,尤其是IE10以下,所以才出现了SockJS这样一个框架,它的原理也很简单,就是如果你的浏览器支持WebSocket那么他就使用webSocket协议通信,入股不支持就使用流传输或者轮询的方式,这样也保证了资源的最大利用率。

1、websocket相关pom文件配置

<!--websocket--><!-- PRovided Websocket API, because tomcat has its own implementation --><dependency>    <groupId>javax.websocket</groupId>    <artifactId>javax.websocket-api</artifactId>    <version>1.1</version>    <scope>provided</scope></dependency><dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-websocket</artifactId>    <version>4.3.3.RELEASE</version></dependency><dependency>    <groupId>javax.servlet</groupId>    <artifactId>javax.servlet-api</artifactId>    <version>3.1.0</version>    <scope>provided</scope></dependency><!--相关jar--><!-- Json --><dependency>    <groupId>com.fasterxml.jackson.core</groupId>    <artifactId>jackson-core</artifactId>    <version>2.8.1</version></dependency><dependency>    <groupId>com.fasterxml.jackson.core</groupId>    <artifactId>jackson-databind</artifactId>    <version>2.8.1</version>

2、WebSocketConfig----配置WebSocket访问的地址

package com.bms.web.notice.websocket.config;import com.bms.web.notice.websocket.handler.SystemWebSocketHandler;import com.bms.web.notice.websocket.interceptor.WebsocketHandshakeInterceptor;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;import org.springframework.web.servlet.config.annotation.EnableWebMvc;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;import org.springframework.web.socket.WebSocketHandler;import org.springframework.web.socket.config.annotation.EnableWebSocket;import org.springframework.web.socket.config.annotation.WebSocketConfigurer;import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;import org.springframework.web.socket.server.HandshakeInterceptor;/** * Created by zhu_kai1 on 2017/2/23. */@Configuration@EnableWebMvc@EnableWebSocketpublic class WebsocketConfig extends WebMvcConfigurerAdapter implements WebSocketConfigurer {    // webSocket    private static final String WEBSOCKET_SERVER ="/webSocketServer";    private static final String ECHO ="/echo";    // 不支持webSocket的话用sockjs    private static final String SOCKJS ="/sockjs/webSocketServer";    @Override    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {        //支持websocket 的访问链接        registry.addHandler(systemWebSocketHandler(), WEBSOCKET_SERVER).addInterceptors(handshakeInterceptor());        registry.addHandler(systemWebSocketHandler(), ECHO).addInterceptors(handshakeInterceptor());        //不支持websocket的访问链接        registry.addHandler(systemWebSocketHandler(), SOCKJS).addInterceptors(handshakeInterceptor()).withSockJS();    }    @Bean    public WebSocketHandler systemWebSocketHandler(){        return new SystemWebSocketHandler();    }    @Bean    public HandshakeInterceptor handshakeInterceptor(){        return new WebsocketHandshakeInterceptor();    }    // Allow serving HTML files through the default Servlet    @Override    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {        configurer.enable();    }}

3、HandshakeInterceptor---握手拦截器

package com.bms.web.notice.websocket.interceptor;import com.msk.sso.client.bean.User;import org.slf4j.Logger;import org.slf4j.LoggerFactory;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.HandshakeInterceptor;import javax.servlet.http.HttpServletRequest;import java.util.Map;/** * Created by zhu_kai1 on 2017/2/23. */public class WebsocketHandshakeInterceptor implements HandshakeInterceptor{    private static Logger logger = LoggerFactory.getLogger(WebsocketHandshakeInterceptor.class);    public WebsocketHandshakeInterceptor() {    }    // 初次握手访问前    @Override    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler webSocketHandler, Map<String, Object> map) throws Exception {        if (request instanceof ServletServerHttpRequest) {            HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();            //使用userName区分WebSocketHandler,以便定向发送消息            User loginUser= (User) servletRequest.getsession().getAttribute("loginUser");            //存入数据,方便在hander中获取,这里只是在方便在webSocket中存储了数据,并不是在正常的httpSession中存储,想要在平时使用的session中获得这里的数据,需要使用session 来存储一下            if(null !=loginUser){                map.put("userName", loginUser.getUserLogin());                logger.info("当前的登陆者为:{}", loginUser.getUserLogin());            }else{                logger.error("没有获取session中的当前登陆者信息");            }        }        return true;    }    @Override    public void afterHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler webSocketHandler, Exception e) {    }}

4、SystemWebSocketHandler----消息处理

package com.bms.web.notice.websocket.handler;import com.bms.web.notice.bean.result.NoticeResult;import com.bms.web.notice.service.CommonService;import com.framework.base.rest.result.BaseRestPaginationResult;import com.framework.core.utils.StringUtils;import com.framework.exception.SystemException;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import org.springframework.web.socket.*;import java.io.IOException;import java.util.ArrayList;import java.util.Collections;import java.util.List;/** * Created by zhu_kai1 on 2017/2/23. */@Componentpublic class SystemWebSocketHandler implements WebSocketHandler {    @Autowired    private  CommonService commonService;    public static final String USERNAME = "userName";    private static Logger logger = LoggerFactory.getLogger(SystemWebSocketHandler.class);    protected final static List<WebSocketSession> sessions = Collections.synchronizedList(new ArrayList<WebSocketSession>());    public SystemWebSocketHandler() {    }    // 连接建立后处理    @Override    public void afterConnectionEstablished(WebSocketSession webSocketSession) throws Exception {        logger.info("webSocket连接已建立");        sessions.add(webSocketSession);        String userLogin = (String) webSocketSession.getAttributes().get(USERNAME);        BaseRestPaginationResult<NoticeResult> result = null;        if(StringUtils.isNotEmpty(userLogin)){            result = commonService.getNoticeInfo(userLogin);        }        if(null !=result){            sendMessageToAll(new TextMessage(StringUtils.toString(result.getTotal())));        }    }    // 接收客户端消息,并发送出去    @Override    public void handleMessage(WebSocketSession webSocketSession, WebSocketMessage<?> webSocketMessage) throws Exception {        logger.info("发送消息" + webSocketMessage.toString());    }    // 抛出异常时处理    @Override    public void handleTransportError(WebSocketSession webSocketSession, Throwable throwable) throws Exception {        if (webSocketSession.isOpen()) {            webSocketSession.close();        }        sessions.remove(webSocketSession);        logger.info("webSocket异常处理" + throwable.getMessage());        throw new SystemException(throwable);    }    // 连接关闭后处理    @Override    public void afterConnectionClosed(WebSocketSession webSocketSession, CloseStatus closeStatus) throws Exception {        logger.info("webSocket连接已关闭......" + closeStatus.getReason());        sessions.remove(webSocketSession);    }    @Override    public boolean supportsPartialMessages() {        return false;    }    /**     * 给所有在线用户发送消息     *     * @param message     */    public void sendMessageToAll(TextMessage message) {        for (WebSocketSession session : sessions) {            try {                if (session.isOpen()) {                    session.sendMessage(message);                }            } catch (IOException e) {                throw new SystemException(e.getMessage());            }        }    }}

5、在对应的spring-mvc.xml配置文件中需要扫描websocketConfig

 <context:component-scan base-package="com.bms.web" use-default-filters="false">        <context:include-filter type="regex" expression="com/.bms/.web/.notice/.websocket/..*"/>    </context:component-scan>

6、jsp需要引用对应的sockjs

<script src="http://cdn.sockjs.org/sockjs-0.3.min.js"></script>

7、js调用方法

webSocket: function () {        var host = window.location.host;        var websocket;        var url = null;        if ('WebSocket' in window) {            url = "ws://" + host + Main.contextPath + "/webSocketServer";            websocket = new WebSocket(url);        } else if ('MozWebSocket' in window) {            url = "ws://" + host + Main.contextPath + "/echo";            websocket = new MozWebSocket(url);        } else {            url = Main.contextPath + "/sockjs/webSocketServer";            websocket = new SockJS(url);//建立连接        }        websocket.onopen = function (event) {        };        websocket.onmessage = function (event) {            $("span#noticeTotal").text(event.data);            $("span#notice").text("共" + event.data + "条通知");        };        websocket.onerror = function (event) {        };        websocket.onclose = function (event) {            $.alertMessage.error("与webSocket服务器断开了连接");        }    }ok,到此大致的websocket已搭建好,但是各位可能会遇到以下问题,请找到对应的错误解决,以下解决方法也是从网上查询到的。

问题1:统计了一下大家遇到第一个问题就是连接websocket时候报404错误

先检查连接websocket的url格式:ws://localhost:8080/web/webSocketServer,这个webSocketServer要匹配websocketConfig中的

registry.addHandler(systemWebSocketHandler(), "/webSocketServer").addInterceptors(handshakeInterceptor());

其次检查下Spring配置文件是否有加这个tag:<mvc:annotation-driven/>(加这个会出现中文乱码,下面会讲到),使用Spring websocket需要这个tag支持

当Spring配置文件有使用<context:component-scan/>扫描包,这个tag<context:annotation-config/>可以不去掉。

问题2:连接websocket时候报200,说明已经进入拦截器握手成功,但是没连接上websocket

如果websocket有配置自己定义的拦截器,先检查下拦截器beforeHandshake这个方法,这个方法有个参数Map<String, Object> attributes,不能给这个map的value设成null,否则进不到自己Handler下的这个方法afterConnectionEstablished,就会报200

问题3:连接websocket时候,如果缺少配置会报415 Unsupported Media Type请求的格式不受请求页面的支持错误

当用户发送请求后,@Requestbody 注解会读取请求body中的数据,默认的请求转换器HttpMessageConverter通过获取请求头Header中的Content-Type来 确认请求头的数据格式,从而来为请求数据适配合适的转换器。例如contentType:applicatin/json,那么转换器会适配 MappingJacksonHttpMessageConverter。响应时候的时候同理,@Responsebody注解会启用 HttpMessageConverter,通过检测Header中Accept属性来适配的响应的转换器。

当在使用SpringMVC做服务器数据接收时,尤其是在做Ajax请求的时候,尤其要注意contentType属性,和accepte 属性的设置,在springmvc-config.xml中配置好相应的转换器。

添加相应转换器:

<bean id="stringHttpMessageConverter"        class="org.springframework.http.converter.StringHttpMessageConverter">        <property name="supportedMediaTypes">            <list>                <value>text/plain;charset=UTF-8</value>            </list>        </property></bean>

<bean        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">        <property name="messageConverters">            <list>                <ref bean="stringHttpMessageConverter" />新增的                <ref bean="byteArrayHttpMessageConverter" />                <ref bean="jsonHttpMessageConverter" />                <ref bean="jsonHttpMessageConverter4JS" />            </list>        </property>    </bean>

问题4:网上例子都有说要在web.xml下的servlet和filter里面加上<async-supported>true</async-supported>

没有影响websocket是可以连接成功的(小编就是这种方式实现的),可以不用加,这个是用来支持异步的servlet3.x,建议不用加,除非有用到这个特性(小编没有试,这个可以自行对比下)

问题5:添加<mvc:annotation-driven/>这个出现中文乱码

刚开始时候,所有浏览器都出现中文乱码,后来解决在Chrome浏览不会出现中文乱码,但是在FF下会出现。原因有三个:第一,MessageConverter转换器没配置相应的<property name="supportedMediaTypes">属性,第二,bean和tag的先后顺序不对,第三,当使用<mvc:annotation-driven/>这个tag时候,请求处理器就会变成RequestMappingHandlerAdapter,跟进代码就会发现不是采用AnnotationMethodHandlerAdapter,所以配置时候要改成:org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter统一解决办法是:要注意bean和tag的先后顺序<bean        class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">//这是根本原因        <property name="messageConverters">            <list>                <ref bean="stringHttpMessageConverter" />                <ref bean="byteArrayHttpMessageConverter" />                <ref bean="jsonHttpMessageConverter" />                <ref bean="jsonHttpMessageConverter4JS" />            </list>        </property>    </bean><context:component-scan base-package="扫描Spring controller包路径" /><context:component-scan base-package="扫描websocket包路径"/><context:annotation-config />  //这个标注可以不加<mvc:annotation-driven/>//这个tag一定要放在上面代码最后,这是也是乱码根源之一

问题6:启动时候出现这个Factory method 'webSocketHandlerMapping' threw exception; nested exception isjava.lang.IllegalStateException: No suitable default RequestUpgradeStrategy found

 

说明你的容器不支持websocket协议Tomcat7,0.26之后才支持websocket

Jboss as 7不支持websocket,需要要安装插件,可以直接升级到wildfly8支持websocket

 


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表