【问题标题】:Connection timeout using standard JSR356 WebSocket client使用标准 JSR356 WebSocket 客户端的连接超时
【发布时间】:2015-04-17 01:22:03
【问题描述】:

我有一个连接到远程 websocket 服务器的 Java 应用程序。作为客户端,我使用的是标准的 Java EE JSR356 WebSocket API

javax.websocket.WebSocketContainer.connectToServer(...)

但是,我还没有找到使用此 API 指定连接超时的方法。当我调用 connectToServer(...) 方法时,它会阻塞直到建立连接(这可能永远不会发生)。

有没有办法使用标准 API 指定连接超时?如果没有,是否有任何解决方法?

【问题讨论】:

    标签: jakarta-ee websocket connection timeout


    【解决方案1】:

    很遗憾,JSR 356 - WebSocket API for Java 并未公开此内容。您将需要使用实现功能,例如Tyrus 中的HANDSHAKE_TIMEOUT(参考实现)。其他实现很可能有类似的东西。

    WEBSOCKET_SPEC 中似乎还没有关于此的票证,因此您可以根据需要添加一张票(我只能找到提到 SSL 属性的问题 -WEBSOCKET_SPEC-210)。

    【讨论】:

    【解决方案2】:

    我自己就搞定了。如果您通过 Future 构造调用 connectToServer,则可以在 get() 方法中使用超时。

    你需要一个线程池:

    private final ExecutorService pool = Executors.newFixedThreadPool(10);
    

    函数如下:

    private Future<Session> asyncConnectToServer(Object annotatedEndpointInstance, URI uri) {
            return pool.submit(new Callable<Session>() {
                @Override
                public Session call() throws Exception {
                    try {
                        WebSocketContainer container = ContainerProvider.getWebSocketContainer();
                        return(container.connectToServer(annotatedEndpointInstance, uri));
                    } catch (DeploymentException | IOException | IllegalStateException  e) {
                        //throw new RuntimeException(e);
                        return(null);
                    }
                }
            });
        }
    

    这就是你的称呼:

     public webSocketClientEndpoint(URI endpointURI, long timeout) {
    
            final Future<Session> futureSes = asyncConnectToServer(this, endpointURI);
    
            try {            
                Session ses = futureSes.get(timeout, TimeUnit.MILLISECONDS);
            } catch(InterruptedException | ExecutionException | TimeoutException  e) {
                System.out.println("Time out...");
            }
        }
    

    参考: https://dzone.com/articles/javautilconcurrentfuture

    https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html#get(long,%20java.util.concurrent.TimeUnit)

    【讨论】:

    • 始终欢迎提供潜在解决方案的链接,但请add context around the link,以便您的其他用户知道它是什么以及为什么存在。始终引用重要链接中最相关的部分,以防目标站点无法访问或永久离线。考虑到仅仅是指向外部站点的链接是Why and how are some answers deleted? 的一个可能原因。
    【解决方案3】:

    你可以简单地覆盖 WsWebSocketContainer 中的 connectToServer 方法

    public class WsWebSocketContainer2 extends WsWebSocketContainer {
    
        @Override
        public Session connectToServer(Object pojo, URI path) throws DeploymentException {
            ClientEndpoint annotation = pojo.getClass().getAnnotation(ClientEndpoint.class);
            if (annotation == null) {
                throw new DeploymentException("wsWebSocketContainer.missingAnnotation");
            }
    
            Endpoint ep = new PojoEndpointClient(pojo, Arrays.asList(annotation.decoders()));
    
            Class<? extends ClientEndpointConfig.Configurator> configuratorClazz = annotation.configurator();
    
            ClientEndpointConfig.Configurator configurator = null;
            if (!ClientEndpointConfig.Configurator.class.equals(configuratorClazz)) {
                try {
                    configurator = configuratorClazz.getConstructor().newInstance();
                } catch (ReflectiveOperationException e) {
                    throw new DeploymentException("wsWebSocketContainer.defaultConfiguratorFail", e);
                }
            }
    
            ClientEndpointConfig.Builder builder = ClientEndpointConfig.Builder.create();
            // Avoid NPE when using RI API JAR - see BZ 56343
            if (configurator != null) {
                builder.configurator(configurator);
            }
            ClientEndpointConfig config = builder.decoders(Arrays.asList(annotation.decoders())).encoders(Arrays.asList(annotation.encoders()))
                    .preferredSubprotocols(Arrays.asList(annotation.subprotocols())).build();
            Map<String, Object> userProperties = config.getUserProperties();
            userProperties.put(Constants.IO_TIMEOUT_MS_PROPERTY, 999999);
            return connectToServer(ep, config, path);
        }
    }
    

    【讨论】:

    • 这并不理想,但它确实适用于此更改:超时用户属性值必须是字符串。 userProperties.put(Constants.IO_TIMEOUT_MS_PROPERTY, "999999");
    猜你喜欢
    • 1970-01-01
    • 2014-04-17
    • 2015-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多