需要一个简单的纯Java 11 WebSocket客户端示例的帮助。

14

在网络上似乎很少有纯Java 11(非框架)的WebSocket 客户端代码示例,所以我希望StackOverflow再次为我提供帮助。

这个是我找到的最接近的例子,但不幸的是,在我这样的新手眼中,它似乎并没有展示如何从WebSocket监听器中获取数据的完整解决方案。

通过查看WebSocket.Listener实现,我认为onText回调方法可以提供我需要的内容,但我无法确定如何同时返回CompletionStage对象和某种字符串数据。

这是我目前的一些测试代码。

感激任何帮助。谢谢

    public class Main {

        public static void main(String[] args) {

           WebSocketClient wsc = new WebSocketClient();
           wsc.startSocket("ws://demos.kaazing.com/echo");

           int i = 0;   

           // Bad, very bad
           do {} while (i == 0);
        }
    }


    public class WebSocketClient implements WebSocket.Listener {

        @Override
        public void onOpen(WebSocket webSocket) {
           //...
            System.out.println("Go...Open".concat(
                    webSocket.getSubprotocol()));
        }

        @Override
        public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
           //...
            System.out.println(data.toString());

            // How do I return the CompletionStage object
            // return CompletionStage<String>
        }

        @Override
        public void onError(WebSocket webSocket, Throwable error) {
           //..
            System.out.println("Bad day! ".concat(webSocket.toString()));
        }

        void startSocket(String connection) {
            CompletableFuture<WebSocket> server_cf = HttpClient.
                    newHttpClient().
                    newWebSocketBuilder().
                    buildAsync(URI.create(connection),
                            new WebSocketClient());
            WebSocket server = server_cf.join();
            server.sendText("Hello!", true);
        }
    }

并不是很在意(因为我只是想要一些帮助),但如果你要给我点踩,能否至少说一下哪里有问题呢? - eodeluga
你知道为什么Listeneron方法需要返回CompletionStage吗?换句话说,这些阶段有什么作用? - pavel
3个回答

16

下面是一个可工作的示例。我对你上面的代码进行了一些更改:

  • onOpen需要在websocket上调用request(1)(调用默认实现)以接收进一步的调用。
  • 将方法startSocket移动到主方法中
  • 使用倒计时门替换繁忙等待
  • 将类WebSocketClient声明为(静态)内部类

但除了这些(表面上的)更改外,程序遵循了您的想法,即首先建立websocket连接,成功构建后将文本Hello!发送到回声服务器。这也可以直接在方法onOpen中完成。

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.CountDownLatch;
        
public class Main {
        
    public static void main(String[] args) throws Exception {
        CountDownLatch latch = new CountDownLatch(1);
        
        WebSocket ws = HttpClient
                .newHttpClient()
                .newWebSocketBuilder()
                .buildAsync(URI.create("ws://demos.kaazing.com/echo"), new WebSocketClient(latch))
                .join();
        ws.sendText("Hello!", true);
        latch.await();
    }
            
    private static class WebSocketClient implements WebSocket.Listener {
        private final CountDownLatch latch;
                
        public WebSocketClient(CountDownLatch latch) { this.latch = latch; }
        
        @Override
        public void onOpen(WebSocket webSocket) {
            System.out.println("onOpen using subprotocol " + webSocket.getSubprotocol());
            WebSocket.Listener.super.onOpen(webSocket);
        }
        
        @Override
        public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
            System.out.println("onText received " + data);
            latch.countDown();
            return WebSocket.Listener.super.onText(webSocket, data, last);
        }
        
        @Override
        public void onError(WebSocket webSocket, Throwable error) {
            System.out.println("Bad day! " + webSocket.toString());
            WebSocket.Listener.super.onError(webSocket, error);
        }
    }
}

顺便提一句,没有协议被协商,因此方法webSocket.getSubprotocol()返回一个空字符串。在控制台的输出如下:

    onOpen using subprotocol 
    onText received Hello!

1
你不需要一味地调用 WebSocket.Listener.super.onXXX 方法,这完全取决于你的需求。特别是,onError 指定了: @implSpec 默认实现什么也不做 - pavel
1
@pavel,你说得对,感谢你的评论。所有的超级调用都可以被删除(onText可以返回null),但是在onOpen方法中,语句webSocket.request(1)必须被执行,以便使websocket调用至少一个接收方法(这在原始示例中缺失)。 - Dominik
你好,如果我想发送身份验证数据到WebSocket服务器以建立连接,应该如何操作?我已经成功使用JavaScript建立了WebSocket连接。以下是我发送到WS连接的JSON对象 => jsonSendObj = { "channel":"", "task":"cn", "acctid":UID, "user":UID, "token": userSessionID }; 我们如何使用Java发送这个对象?我们可以使用相同的SetString方法吗? - Jerokdeep

1

管理WebSocket响应返回CompletionStage的模式如下:

@Override
public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
    // return inmmediately but response is geenrated lazyly.
    return CompletableFuture.supplyAsync(() -> {
        String response = "Received  ...";
        // do slow task. Access to database or access to a server.
        return response;
    });
}

只有在响应快速生成时,才建议使用这种简单的实现方式。

@Override
public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
    // fast response.
    String response = "The text has " + data.length() + " chars";
    return CompletableFuture.completedFuture(response);
}

0

我在尝试运行一些示例时遇到了一些问题。具体来说,我很难找到实际演示如何打开、发送和接收简单文本消息的示例。其中一个重要的部分是有一个可连接的服务器。以下是我成功运行的内容。

    package webSockets;
    
    import java.io.IOException;
    import java.net.URI;
    
    import javax.websocket.CloseReason;
    import javax.websocket.ContainerProvider;
    import javax.websocket.Endpoint;
    import javax.websocket.EndpointConfig;
    import javax.websocket.MessageHandler;
    import javax.websocket.Session;
    import javax.websocket.WebSocketContainer;
    
    public class SimpleWebsocketClient extends Endpoint {
        private Session session;
    
        public SimpleWebsocketClient() {}
    
        public SimpleWebsocketClient(URI endpointURI) {
            try {
                WebSocketContainer container = ContainerProvider.getWebSocketContainer();
                container.connectToServer(this, endpointURI);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
      
        @Override
        public void onClose(Session session, CloseReason reason){
            System.out.println("Disconnected as a result of "+ reason.getReasonPhrase());
        }
        @Override
        public void onError(Session session, Throwable error){
            System.out.println("Error communicating with server: " + error.getMessage());
        }
        @Override
        public void onOpen(Session s, EndpointConfig config) {
            System.out.println("Session opened");
            session = s;
            session.addMessageHandler(new MessageHandler.Whole<String>() {
    
                @Override
                public void onMessage(String msg) {
                    System.out.println("Text Message Received:" + msg);
                }
            });
            try {
                session.getBasicRemote().sendText("Hello there.");
                session.getBasicRemote().sendText("Hope you are well!");
            } catch (IOException ex) {
                throw new RuntimeException(ex);
            }
        }
        
        public static void main(String...arg) {
            URI uri = URI.create("ws://connect.websocket.in/v3/1?api_key=oCdCMcMPQpbvNjUIzqtvF1d2X2okWpDQj4AwARJuAgtjhzKxVEjQU6IdCjwm&notify_self");
            new SimpleWebsocketClient(uri);
            while(true) {}
        }
    }

请注意,这是使用JavaEE的javax.websockets支持,而不是OP要求的Java 11 SE的java.net.http.WebSocket支持。 - dbreaux
没错,抱歉打扰了讨论。 - ae6ty

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接