Websocket出现的错误

前端使用sockjs,后台使用spring的websocket框架

结果在一个网络较慢的地方,发现tomcat报错信息:

Oct 28, 2015 10:10:43 AM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [mvc-dispatcher] in context with path [/rscc] threw exception [Request processing failed; nested exception is org.springframework.web.socket.sockjs.SockJsException: Uncaught failure in SockJS request, uri=http://xxx/user/854/qckzogtf/xhr_streaming; nested exception is org.springframework.web.socket.sockjs.SockJsException: Uncaught failure for request http://xxx/user/854/qckzogtf/xhr_streaming; nested exception is java.lang.IllegalArgumentException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml. Also you must use a Servlet 3.0+ container] with root cause
java.lang.IllegalArgumentException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml. Also you must use a Servlet 3.0+ container
    at org.springframework.util.Assert.isTrue(Assert.java:65)
    at org.springframework.http.server.ServletServerHttpAsyncRequestControl.<init>(ServletServerHttpAsyncRequestControl.java:59)
    at org.springframework.http.server.ServletServerHttpRequest.getAsyncRequestControl(ServletServerHttpRequest.java:202)
    at org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession.initRequest(AbstractHttpSockJsSession.java:238)
    at org.springframework.web.socket.sockjs.transport.session.AbstractHttpSockJsSession.handleInitialRequest(AbstractHttpSockJsSession.java:203)
    at org.springframework.web.socket.sockjs.transport.session.StreamingSockJsSession.handleInitialRequest(StreamingSockJsSession.java:54)
    at org.springframework.web.socket.sockjs.transport.handler.AbstractHttpSendingTransportHandler.handleRequestInternal(AbstractHttpSendingTransportHandler.java:66)
    at org.springframework.web.socket.sockjs.transport.handler.AbstractHttpSendingTransportHandler.handleRequest(AbstractHttpSendingTransportHandler.java:58)
    at org.springframework.web.socket.sockjs.transport.TransportHandlingSockJsService.handleTransportRequest(TransportHandlingSockJsService.java:254)
    at org.springframework.web.socket.sockjs.support.AbstractSockJsService.handleRequest(AbstractSockJsService.java:317)
    at org.springframework.web.socket.sockjs.support.SockJsHttpRequestHandler.handleRequest(SockJsHttpRequestHandler.java:88)
    at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:51)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
    at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:108)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:950)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1040)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:607)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

根据报错信息来看,应该是缺少了<async-supported>true</async-supported>这个配置,这个是3.0开始支持的,async的请求需要开启async-supported。

但是该项目在我们本地从来没有出现过这个问题,情况说明及解决分析过程如下:

一、前端连接情况:ws = new SockJS( ‘url‘, undefined, {});

  不加参数,sockjs默认会选择最优方式来连接,情况如下:

  

  关于协议选择:在第一种连接方式超时时,sockjs会选择次优方式进行连接:

var _all_protocols = [‘websocket‘,
                      ‘xdr-streaming‘,
                      ‘xhr-streaming‘,
                      ‘iframe-eventsource‘,
                      ‘iframe-htmlfile‘,
                      ‘xdr-polling‘,
                      ‘xhr-polling‘,
                      ‘iframe-xhr-polling‘,
                      ‘jsonp-polling‘];
所有协议(连接方式)

  检测所有可用协议并按优先级排序

utils.probeProtocols = function() {
    var probed = {};
    for(var i=0; i<_all_protocols.length; i++) {
        var protocol = _all_protocols[i];
        // User can have a typo in protocol name.
        probed[protocol] = SockJS[protocol] &&
                           SockJS[protocol].enabled();
    }
    return probed;
};

utils.detectProtocols = function(probed, protocols_whitelist, info) {
    var pe = {},
        protocols = [];
    if (!protocols_whitelist) protocols_whitelist = _all_protocols;
    for(var i=0; i<protocols_whitelist.length; i++) {
        var protocol = protocols_whitelist[i];
        pe[protocol] = probed[protocol];
    }
    var maybe_push = function(protos) {
        var proto = protos.shift();
        if (pe[proto]) {
            protocols.push(proto);
        } else {
            if (protos.length > 0) {
                maybe_push(protos);
            }
        }
    }

    // 1. Websocket
    if (info.websocket !== false) {
        maybe_push([‘websocket‘]);
    }

    // 2. Streaming
    if (pe[‘xhr-streaming‘] && !info.null_origin) {
        protocols.push(‘xhr-streaming‘);
    } else {
        if (pe[‘xdr-streaming‘] && !info.cookie_needed && !info.null_origin) {
            protocols.push(‘xdr-streaming‘);
        } else {
            maybe_push([‘iframe-eventsource‘,
                        ‘iframe-htmlfile‘]);
        }
    }

    // 3. Polling
    if (pe[‘xhr-polling‘] && !info.null_origin) {
        protocols.push(‘xhr-polling‘);
    } else {
        if (pe[‘xdr-polling‘] && !info.cookie_needed && !info.null_origin) {
            protocols.push(‘xdr-polling‘);
        } else {
            maybe_push([‘iframe-xhr-polling‘,
                        ‘jsonp-polling‘]);
        }
    }
    return protocols;
}

  连接时若超时切换协议的代码

while(1) {
        var protocol = that.protocol = that._protocols.shift();
        if (!protocol) {
            return false;
        }
        // Some protocols require access to `body`, what if were in
        // the `head`?
        if (SockJS[protocol] &&
            SockJS[protocol].need_body === true &&
            (!_document.body ||
             (typeof _document.readyState !== ‘undefined‘
              && _document.readyState !== ‘complete‘))) {
            that._protocols.unshift(protocol);
            that.protocol = ‘waiting-for-load‘;
            utils.attachEvent(‘load‘, function(){
                that._try_next_protocol();
            });
            return true;
        }
//下面的to = 就是计算连接超时时间的,调用delay,在连接超时时关闭这个协议的连接。
        if (!SockJS[protocol] ||
              !SockJS[protocol].enabled(that._options)) {
            that._debug(‘Skipping transport:‘, protocol);
        } else {
            var roundTrips = SockJS[protocol].roundTrips || 1;
            var to = ((that._options.rto || 0) * roundTrips) || 5000;
            that._transport_tref = utils.delay(to, function() {
                if (that.readyState === SockJS.CONNECTING) {
                    // I can‘t understand how it is possible to run
                    // this timer, when the state is CLOSED, but
                    // apparently in IE everythin is possible.
                    that._didClose(2007, "Transport timeouted");
                }
            });

            var connid = utils.random_string(8);
            var trans_url = that._base_url + ‘/‘ + that._server + ‘/‘ + connid;
            that._debug(‘Opening transport:‘, protocol, ‘ url:‘+trans_url,
                        ‘ RTO:‘+that._options.rto);
            that._transport = new SockJS[protocol](that, trans_url,
                                                   that._base_url);
            return true;
        }
    }

   观察上面报错信息中的uri:uri=http://xxx/user/854/qckzogtf/xhr_streaming; 因为正常情况下连接为websocket连接,这个uri应该为ws=http://xxx/user/854/qckzogtf/websocket,由此可见,这里应该是切换为xhr_streaming协议了

  前面也说过,他们网络环境较慢,所以才想,应该是连接超时导致的,有上面可知,to为超时时间,计算公式为

   var to = ((that._options.rto || 0) * roundTrips) || 5000;

  关键在于that._options.rto,这个是new的时候的设置项,所以可以设置rto超时长一点。

  结果发现设置中只有

  that._options = {devel: false, debug: false, protocols_whitelist: [],info: undefined, rtt: undefined};

  这几项,并没有rto,于是继续看

var SockJS = function(url, dep_protocols_whitelist, options) {
    if (this === _window) {
        // makes `new` optional
        return new SockJS(url, dep_protocols_whitelist, options);
    }

    var that = this, protocols_whitelist;
    that._options = {devel: false, debug: false, protocols_whitelist: [],
                     info: undefined, rtt: undefined};
    if (options) {
        utils.objectExtend(that._options, options);
    }
    that._base_url = utils.amendUrl(url);
    that._server = that._options.server || utils.random_number_string(1000);
    if (that._options.protocols_whitelist &&
        that._options.protocols_whitelist.length) {
        protocols_whitelist = that._options.protocols_whitelist;
    } else {
        // Deprecated API
        if (typeof dep_protocols_whitelist === ‘string‘ &&
            dep_protocols_whitelist.length > 0) {
            protocols_whitelist = [dep_protocols_whitelist];
        } else if (utils.isArray(dep_protocols_whitelist)) {
            protocols_whitelist = dep_protocols_whitelist
        } else {
            protocols_whitelist = null;
        }
        if (protocols_whitelist) {
            that._debug(‘Deprecated API: Use "protocols_whitelist" option ‘ +
                        ‘instead of supplying protocol list as a second ‘ +
                        ‘parameter to SockJS constructor.‘);
        }
    }
    that._protocols = [];
    that.protocol = null;
    that.readyState = SockJS.CONNECTING;
    that._ir = createInfoReceiver(that._base_url);
    that._ir.onfinish = function(info, rtt) {
        that._ir = null;
        if (info) {
            if (that._options.info) {
                // Override if user supplies the option
                info = utils.objectExtend(info, that._options.info);
            }
            if (that._options.rtt) {
                rtt = that._options.rtt;
            }
            that._applyInfo(info, rtt, protocols_whitelist);
            that._didClose();
        } else {
            that._didClose(1002, ‘Can\‘t connect to server‘, true);
        }
    };
};

utils.objectExtend(that._options, options);这一句比较可疑是转移属性的,

that._applyInfo(info, rtt, protocols_whitelist);比较可疑,看一下

SockJS.prototype._applyInfo = function(info, rtt, protocols_whitelist) {
    var that = this;
    that._options.info = info;
    that._options.rtt = rtt;
    that._options.rto = utils.countRTO(rtt);
    that._options.info.null_origin = !_document.domain;
    var probed = utils.probeProtocols();
    that._protocols = utils.detectProtocols(probed, protocols_whitelist, info);
};

rto设置找到了!that._options.rto = utils.countRTO(rtt);

utils.countRTO = function (rtt) {
    var rto;
    if (rtt > 100) {
        rto = 3 * rtt; // rto > 300msec
    } else {
        rto = rtt + 200; // 200msec < rto <= 300msec
    }
    return rto;
}

好了,只用设置rtt既可以设置rto了。。。就这样,连接超时的问题算是解决了。

 

二、但是还没完,就算websocket连接超时导致协议切换为xhr_streaming,也不会导致后台报错的情况出现,下面解决这个问题:

报错处的代码:在AbstractHttpSockJsSession类中:

private void initRequest(ServerHttpRequest request, ServerHttpResponse response,
            SockJsFrameFormat frameFormat) {

        Assert.notNull(request, "Request must not be null");
        Assert.notNull(response, "Response must not be null");
        Assert.notNull(frameFormat, "SockJsFrameFormat must not be null");

        this.response = response;
        this.frameFormat = frameFormat;
        this.asyncRequestControl = request.getAsyncRequestControl(response);
    }

最后一句报错咯,看代码:

public ServerHttpAsyncRequestControl getAsyncRequestControl(ServerHttpResponse response) {
        if (this.asyncRequestControl == null) {
            Assert.isInstanceOf(ServletServerHttpResponse.class, response);
            ServletServerHttpResponse servletServerResponse = (ServletServerHttpResponse) response;
            this.asyncRequestControl = new ServletServerHttpAsyncRequestControl(this, servletServerResponse);//这一句报错了
        }
        return this.asyncRequestControl;
    }

new ServletServerHttpAsyncRequestControl时报错

时间: 2024-08-06 14:48:26

Websocket出现的错误的相关文章

signalr使用websocket报500错误

signalr使用websocket报500错误,WebSocket connection to 'ws://localhost:22862/signalr/connect?transport=webSockets&clientProtocol=1.5&connectionToken=Ab7SuqLggw%2BJL5kWeWcdv%2FI%2FdnRcikASgBnVNnsJu1qtIGq5tV7iXvQkDim%2FYFCJ9RNJvWTe9Zgjte2siJz2KXHX3n8ERyw4

websocket通信1009错误,

问题说明: springboot继承 WebSocketConfigurer实现websocket通信服务,服务器端报错,"The decoded text message was too big for the output buffer and the endpoint does not support partial messages",浏览器端显示服务器上的该次会话已经关闭.1009错误,内容长度超限. 问题解决 在应用启动类中通过注解注入方式设置通信的文本和二进制消息的大小.

websocket实战(3) 错误处理及配置管理

回顾 websocket实战(1) 入门 websocket实战(2) 信息处理发送.接收和编码 通过前面说明,已经轻松构建一个简单的websocket ServerEndPoint了.可以为EndPoint加上解码器,编码器,为EndPoint提供支持.但是,作为一个服务器,遇到错误怎么办?websocket作为一个简单的容器组件,也具备简单配置管理功能. 1.错误处理 [email protected] 其实很简单,就是在ServerEndPoint类中,添加一个方法,要求该方法被@onEr

java使用websocket,并且获取HttpSession,源码分析

一:本文使用范围 此文不仅仅局限于spring boot,普通的spring工程,甚至是servlet工程,都是一样的,只不过配置一些监听器的方法不同而已. 本文经过作者实践,确认完美运行. 二:Spring boot使用websocket 2.1:依赖包 websocket本身是servlet容器所提供的服务,所以需要在web容器中运行,像我们所使用的tomcat,当然,spring boot中已经内嵌了tomcat. websocket遵循了javaee规范,所以需要引入javaee的包 <

使用WebSocket实现图文直播功能

WebSocket,简单理解就是H5自带的一套Socket API,用它来实现客户端与服务端的长连接. 使用案例: 实现一个活动的实时报道,也就是图文直播功能 首先来看下项目的结构,很简单,就两个类 MyWebSocket.java 这个就是socket的服务端,功能方面只需添加相应的注解即可,注意类名上的注解@ServerEndpoint("/websocket"),这个/websocket就好比是配置在web.xml中的servlet url一样. H5客户端对应这个服务端实现信息

WebSocket简单介绍

Java后端WebSocket的Tomcat实现 一.WebSocket简单介绍 随着互联网的发展,传统的HTTP协议已经很难满足Web应用日益复杂的需求了.近年来,随着HTML5的诞生,WebSocket协议被提出,它实现了浏览器与服务器的全双工通信,扩展了浏览器与服务端的通信功能,使服务端也能主动向客户端发送数据. 我们知道,传统的HTTP协议是无状态的,每次请求(request)都要由客户端(如 浏览器)主动发起,服务端进行处理后返回response结果,而服务端很难主动向客户端发送数据:

websocket(二) websocket的简单实现,识别用户属性的群聊

没什么好说的,websocket实现非常简单,我们直接看代码. 运行环境:jdk8 tomcat8 无须其他jar包. 具体环境支持自己百度 package com.reach.socketController; import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CopyOnWriteArraySet; import javax.annot

WebSocket至HelloWorld

1.基本环境:JDK-1.8.71.Tomcat:apache-tomcat-8.0.33 2.客户端代码: <%@ page language="java" import="java.util.*" pageEncoding="utf-8"%> <% String path = request.getContextPath(); String basePath = request.getScheme()+"://&q

JavaEE 7 新特性之WebSocket

开发环境: JDK:1.7及以上 JavaEE:1.7,因为只有javaee7才有websocke的api,也可以使用1.6单都导入websocket-api.jar试试(本人不清楚) 注意:没有使用springmvc websocket包,利用的是JavaEE7  javax.websocket包,不需要任何xml配置 springMVC也集成了websocket,不了解 -------------------------------------------------------------