在后端Java类中运行时连接到Web-Socket服务器

3

我使用Spring实现了以下Web-Socket服务器。我们不想使用STOMP和JSocks。

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

  public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    registry.addHandler(new WebSocketServerHandler(), "/websocket/user/{userId}");
  }

}




@Service
public class WebSocketServerHandler extends TextWebSocketHandler {

   private List<WebSocketSession> sessions = new ArrayList<WebSocketSession>();

   @Override
   public void handleTextMessage(WebSocketSession session, TextMessage message)
        throws Exception {

        session.sendMessage(message);           
   }

   @Override
   public void afterConnectionEstablished(WebSocketSession session) throws Exception {
     sessions.add(session);
   }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
      sessions = null;
    }

}

现在,我可以成功地从前端客户端(浏览器)连接到Web-Socket服务器。现在,我想在运行时从我的一些Java类中连接到Web-Socket服务器,然后想向该Web-Socket服务器发送消息。有人知道我该如何实现吗?
我已经添加了一个测试Rest Controller,如下所示:
 @RequestMapping(value = "/web-socket/message/{message}")
 public void sendMessage(@PathVariable("message") final String message) throws Exception {
   final String WS_URI = "ws://localhost:8080/web-socket/user/23";

   final CountDownLatch latch = new CountDownLatch(1);
   WebSocketClientHandler handler = new WebSocketClientHandler(latch);
   WebSocketClient client = new StandardWebSocketClient();
   WebSocketSession session = client.doHandshake(handler, WS_URI).get();
   session.sendMessage(new TextMessage(message));
   latch.await(5, TimeUnit.SECONDS);
   session.close();

我能够成功地通过调用Google Rest客户端并使用此测试RestController向WebSocket服务器发送消息,但仅限于第一次。如果我需要再次发送消息,则必须重新启动Tomcat服务器。这里是否有任何问题?

1个回答

8

spring-websocket提供WebSocket客户端支持。基本的合同是WebSocketClient。Spring没有实现完整的WebSocket支持,但提供了对其他实现的抽象层。常见的Spring客户端抽象是StandardWebSocketClient。一个简单的例子看起来像这样:

public static void main(String... args) throws Exception {
    final CountDownLatch latch = new CountDownLatch(1);
    EchoHandler handler = new EchoHandler(latch);
    WebSocketClient client = new StandardWebSocketClient();
    WebSocketSession session = client.doHandshake(handler, ECHO_URL).get();
    session.sendMessage(new TextMessage("Hello World"));
    latch.await(5000, TimeUnit.SECONDS);
    session.close();
}

其中EchoHandler是客户端的WebSocketHandler(与服务器端相同)。

public class EchoHandler extends TextWebSocketHandler {

    private final CountDownLatch latch;

    public EchoHandler(CountDownLatch latch) {
        this.latch = latch;
    }

    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) {
        System.out.println("------- received client message ------");
        System.out.println(message.getPayload());
        System.out.println("--------- end client message ---------");
        latch.countDown();
    }
}

如果您在一个字符串环境中运行客户端,还可以将WebSocketClient包装在WebSocketConnectionManager中。如果需要的话,这将为您提供一些生命周期帮助器。请参见Spring Boot示例中的示例。
至于依赖项,如我所说,Spring没有实现完整的WebSocket客户端支持,因此您需要一个实现。如果您在支持WebSocket的服务器上运行客户端,则不需要添加任何内容。支持实现应该已经在服务器的类路径中。主要支持的WebSocket实现是Jetty、Tomcat、Undertow(主要是Wildfly或独立的Undertow)、Tyrus(即Glassfish、WebLogic)。
如果您在独立的应用程序中运行客户端,则需要添加WebSocket实现。不幸的是,从我测试的结果来看,没有一个实现提供一个完整的可工作的“仅客户端”jar。它们要么需要,要么已经拉入了完整(包括服务器)的实现。因此,仅使用客户端仍然需要拉入一堆服务器jar。以下是我从测试中总结的通用方法。
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-websocket</artifactId>
    <version>${spring.version}</version>
</dependency>
<dependency>
    <groupId>javax.websocket</groupId>
    <artifactId>javax.websocket-api</artifactId>
    <version>${websocket.version}</version>
</dependency>

Tomcat

<dependency>
    <groupId>org.apache.tomcat</groupId>
    <artifactId>tomcat-websocket</artifactId>
    <version>${tomcat.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.tomcat.embed</groupId>
    <artifactId>tomcat-embed-core</artifactId>
    <version>${tomcat.version}</version>
</dependency>

Undertow

<dependency>
    <groupId>io.undertow</groupId>
    <artifactId>undertow-websockets-jsr</artifactId>
    <version>${undertow.version}</version>
</dependency>

Tyrus

<!-- tyrus-client is pulled in by this. -->
<dependency>
    <groupId>org.glassfish.tyrus</groupId>
    <artifactId>tyrus-server</artifactId>
    <version>${tyrus.version}</version>
</dependency>

Jetty

<dependency>
    <groupId>org.eclipse.jetty.websocket</groupId>
    <artifactId>websocket-client</artifactId>
    <version>${jetty.version}</version>
</dependency>

使用Jetty时,它不使用标准Java WebSocket API,因此不能在StandardWebSocketClient中使用。相反,您需要执行以下操作

JettyWebSocketClient client = new JettyWebSocketClient();
client.start();

以上所有内容都是一样的。

我刚刚查看了Spring源代码,看到了他们使用的各种不同依赖项。你可以查看并尝试使用这些依赖项。也许有不同的更有效(更轻)的依赖组合仍然可以工作。上面只是我测试过并能够正常运行的内容。


非常感谢。 "client.doHandshake(handler, ECHO_URL).get();" 会创建一个新的线程吗?如果是的话,我们如何停止该线程。因为我创建了一个 RestController,并能够使用 Google Restclient 发送消息,但是当我尝试第二次发送消息时,没有成功。每次需要发送消息时,我都必须重新启动 tomcat 服务器。 - Ijaz
我的示例仅基于您的回声服务器示例。我不确定您的实际服务器是如何实现的,以及您想要实现什么,因此很难说问题出在哪里。 - Paul Samsotha

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