首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Spring websocket在30分钟后自动关闭(超时)

Spring websocket在30分钟后自动关闭(超时)
EN

Stack Overflow用户
提问于 2018-05-29 23:03:47
回答 4查看 7.8K关注 0票数 5

我正在尝试使用Spring Boot (1.5.13)实现一个websocket。

消息传递工作正常,但大约30分钟后,服务器终止连接(原因1008 -“此连接是在已结束的已验证的HTTP会话下建立的”)。我尝试过设置不同的超时,但似乎没有任何效果。

代码语言:javascript
复制
@Service
@RequiredArgsConstructor
@Slf4j
public class OCPPSocketHandler extends TextWebSocketHandler {
    @Override
    public void handleTextMessage(WebSocketSession webSocketSession, TextMessage textMessage)
        throws IOException {
      ...
    }
}

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

    public static final String ENDPOINT = "/pp/v2.0";

    @Autowired
    private CustomSocketHandler socketHandler;

    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(
            new CustomExceptionWebSocketHandlerDecorator(socketHandler), ENDPOINT
        )
        .setAllowedOrigins("*");
    }
}

application.properties:

代码语言:javascript
复制
#6h as milliseconds
server.connection-timeout=3600000 
server.servlet.session.timeout=6h

每30分钟发送一次TextMessage (WebSocket)以保持连接处于活动状态。

我见过this question about session timeouts,但我看不出有什么解决方案

EN

回答 4

Stack Overflow用户

发布于 2019-07-29 22:37:07

我发现我的WebSocket在30分钟后也关闭了。

根本原因是在SpringBoot中,默认情况下http会话将在30分钟后关闭。

在SpringBoot配置属性中,server.servlet.session.timeout将更改默认行为,但可能有some limit

此外,WebSocket连接有保持活动状态的乒乓消息,因此在乒乓停止之前,永远不要关闭连接。

经过一些跟踪,我找到了一个解决这个问题的方法:

  1. 关闭连接的计时器在这里io.undertow.server.session.InMemorySessionManager.SessionImpl在我的例子中。我们可以看到
  2. io.undertow.server.session.InMemorySessionManager.SessionImpl#setMaxInactiveInterval将重置计时器。
  3. 每次超时之前一旦调用setMaxInactiveInterval,连接就永远不会关闭。

下面是我的实现:

HandshakeRequest存储到配置器中的javax.websocket.EndpointConfig#getUserPropertiesjavax.websocket.server.ServerEndpointConfig.Configurator#modifyHandshake

  • Our客户端将发送字符串消息以保持活动状态,因此在方法中,从javax.websocket.Session#getUserProperties获取HandshakeRequest,然后

代码语言:javascript
复制
HttpSession httpSession = (HttpSession) handshakeRequest.getHttpSession();
httpSession.setMaxInactiveInterval((int) (session.getMaxIdleTimeout() / 1000));

这就是全部,希望它能有所帮助。

票数 4
EN

Stack Overflow用户

发布于 2018-05-29 23:11:22

如果服务器或客户端之间的任何连接关闭了该连接,则可以关闭该连接,如果防火墙注意到所建立的连接上没有任何活动,也可以关闭该连接。

因此,您还需要在客户端检查超时。30分钟是这种连接的默认超时,这是合理的,因此它看起来在客户端使用默认值。

此外,定期检查连接的状态是一个很好的设计,例如发送一种ping (来自客户端的消息) /pong (来自服务器的响应)消息。例如,如果你每分钟做一次,你就对连接状态有了一个概念,连接永远不会因为不活动而关闭。

票数 0
EN

Stack Overflow用户

发布于 2021-07-24 21:23:15

在我的例子中,timeout of HttpSession in SpringBoot设置为server.servlet.session.timeout: 1 (1 HttpSession)

  1. 将Spring安全配置中的websocket端点设置为httpBasic

@Override protected void configure(HttpSecurity http)抛出异常{ http.csrf().disable();http.antMatcher("/ocpp/**") .authorizeRequests() .anyRequest().permitAll()//.hasAnyRole("CS","SYSTEM_ADMIN") .and().httpBasic();}

  1. 在websocket客户端中使用基本身份验证

String id = "testuser";String pw = "1234";String idpw =新建授权;Map httpHeaders =新授权,String>();httpHeaders.put(“String(Base64.getEncoder().encode((id+":"+pw).getBytes()));”,"Basic "+idpw);WebSocketClient webSocketClient =新WebSocketClient(新URI("ws://localhost:9112/ocpp/testuser"),新Draft_6455(Collections.emptyList(),Collections.singletonList(新协议(“ocpp2.0.1”),httpHeaders ){ ...

在这种情况下,如果端点是受保护资源,则在终止HttpSession时发生websocket关闭1008。

Reference:阅读第7.2段

**解决方案是在websocket addInterceptor中直接实现http基本处理。(Spring安全httpBasic未使用) **

WebSocketConfig.java中添加addInterceptor

代码语言:javascript
复制
.addInterceptors(new HandshakeInterceptor() {
    @Override
    public boolean beforeHandshake(ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse, WebSocketHandler wsHandler, Map<String, Object> attributes) throws Exception {
        log.debug("===============================================================");
        log.debug("beforeHandshake[]"+attributes);
        ServletServerHttpRequest ssreq = (ServletServerHttpRequest)serverHttpRequest;
        ServletServerHttpResponse ssres = (ServletServerHttpResponse)serverHttpResponse;
        HttpServletRequest req = ssreq.getServletRequest();
        HttpServletResponse res = ssres.getServletResponse();
        HttpSession session = req.getSession();
        log.debug("session["+session.getId());
        log.debug("session["+session.getMaxInactiveInterval());

        //authentication
        try {
            String header = req.getHeader("Authorization");
            log.debug("header[" + header + "]");
            if (header == null) {
                log.debug("The Authorization header is empty");
//                                    throw new BadCredentialsException("The Authorization header is empty");
            } else {
                header = header.trim();
                if (!StringUtils.startsWithIgnoreCase(header, "Basic")) {
                    log.debug("The Authorization header does not start with Basic.");
                } else if (header.equalsIgnoreCase("Basic")) {
                    throw new BadCredentialsException("Empty basic authentication token");
                } else {
                    byte[] base64Token = header.substring(6).getBytes(StandardCharsets.UTF_8);
                    byte[] decoded;
                    try {
                        decoded = Base64.getDecoder().decode(base64Token);
                    } catch (IllegalArgumentException var8) {
                        throw new BadCredentialsException("Failed to decode basic authentication token");
                    }
                    String token = new String(decoded, "UTF-8");
                    int delim = token.indexOf(":");
                    if (delim == -1) {
                        throw new BadCredentialsException("Invalid basic authentication token");
                    } else {
                        log.info("TOKEN [" +token+"]");
                        String principal = token.substring(0, delim);
                        String credencial = token.substring(delim + 1);
                        //your 
                        
                        if(principal.equals("testuser") && credencial.equals("1234")){
                            log.debug("login OK");
                        }else{
                            throw new BadCredentialsException("Invalid basic authentication token");
                        }
                    }
                }
            }
        }catch(Exception e){
            log.error("Basic Authentication error", e);

            res.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            res.addHeader("WWW-Authenticate", "Basic realm=" + "Realm" + "");
            PrintWriter pw = res.getWriter();
            pw.println("Invalid status code received: 401 Status line: HTTP/1.1 401");
            return false;
        }

        return true;
    }
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50587573

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档