目 录CONTENT

文章目录

SpringBoot整合WebSocket基于STOMP协议构建一个交互式的web应用

成培培
2024-10-30 / 0 评论 / 0 点赞 / 49 阅读 / 0 字

需求来源

最近想做一个小工具方便我在不同设备间传递文本,比如我用两台电脑,一台开发用笔记本,一台拨VPN连堡垒机,可能就经常需要将开发机上的脚本、代码文本发送到另一台电脑上就比较麻烦。所以想到搭建一个小站点像一个匿名聊天室,这边发送消息,那边及时收到。所以就准备用websocket做个小应用。

参考了网上其他一些博客文章的整合方式,感觉比较麻烦,于是参考了spring官方的一篇文章去实现

实现方案

参考官方文档:https://spring.io/guides/gs/messaging-stomp-websocket

源码:https://github.com/spring-guides/gs-messaging-stomp-websocket

官方使用了STOMP协议进行消息传递来创建交互式 Web 应用程序。STOMP 是一个较低级别的 WebSocket 之上运行的子协议,这个协议是基于发布订阅模式进行消息传递的,这样我们在代码层面就不用管理连接的session了,各个客户端订阅相关topic,基于topic进行数据传递。

引入以下依赖

<dependency>

    <groupId>org.springframework.boot</groupId>

    <artifactId>spring-boot-starter-websocket</artifactId>

</dependency>

然后添加如下配置代码,基本的配置就算完了,非常简单

@Configuration

@EnableWebSocketMessageBroker

public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

  @Override

  public void configureMessageBroker(MessageBrokerRegistry config) {

    config.enableSimpleBroker("/topic");

    config.setApplicationDestinationPrefixes("/app");

  }

  @Override

  public void registerStompEndpoints(StompEndpointRegistry registry) {

    registry.addEndpoint("/gs-guide-websocket");

  }

}

客户端使用了stomp.js,具体可以参考官方的文档

另外如果要监听客户端上线掉线等事件,可以直接监听相关的事件:SessionDisconnectEvent、SessionConnectedEvent、SessionSubscribeEvent

直接将代码加入到上面的配置类WebSocketConfig就可以

@EventListener

public void onSessionDisconnectEvent(SessionDisconnectEvent sessionDisconnectEvent) {

	log.info("断开连接 " + ((Map<?, ?>) sessionDisconnectEvent.getMessage().getHeaders().get("simpSessionAttributes")).get("ip"));

	String content = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss : ") + "客户端下线,"

			+ ((Map<?, ?>) sessionDisconnectEvent.getMessage().getHeaders().get("simpSessionAttributes")).get("ip");

	HelloMessage message = HelloMessage.builder().content(content)

			.onlineNum(((SubProtocolWebSocketHandler) subProtocolWebSocketHandler).getStats().getWebSocketSessions())

			.action(HelloMessage.ACTION_EVENT).build();

	messageManager.addMessage(message);

	simpMessagingTemplate.convertAndSend("/topic/greetings", message);

}

@EventListener

public void onSessionConnectedEvent(SessionConnectedEvent sessionConnectedEvent) {

//		log.info("连接上 " + ((Map<?, ?>) sessionConnectedEvent.getMessage().getHeaders().get("simpSessionAttributes")).get("ip"));

}

@EventListener

public void onSessionConnectEvent(SessionConnectEvent sessionConnectEvent) {

	log.info("连接 " + ((Map<?, ?>) sessionConnectEvent.getMessage().getHeaders().get("simpSessionAttributes")).get("ip"));

}

@EventListener

public void onSessionSubscribeEvent(SessionSubscribeEvent sessionSubscribeEvent) {

	String clientIp = (String) ((Map<?, ?>) sessionSubscribeEvent.getMessage().getHeaders().get("simpSessionAttributes")).get("ip");

	log.info("订阅 " + clientIp);

	String content = DateUtil.format(new Date(), "yyyy-MM-dd HH:mm:ss : ") + "客户端上线," + clientIp;

	HelloMessage message = HelloMessage.builder().content(content).onlineNum(((SubProtocolWebSocketHandler) subProtocolWebSocketHandler).getStats().getWebSocketSessions())

			.action(HelloMessage.ACTION_EVENT).build();

	messageManager.addMessage(message);

	simpMessagingTemplate.convertAndSend("/topic/greetings", message);

}

我这里是监听客户端上下线,统计了一下在线人数

最终接收消息就跟写一个普通的Controller差不多:

@Controller

public class GreetingController {

  @MessageMapping("/hello")

  @SendTo("/topic/greetings")

  public Greeting greeting(HelloMessage message) throws Exception {

    Thread.sleep(1000); // simulated delay

    return new Greeting("Hello, " + HtmlUtils.htmlEscape(message.getName()) + "!");

  }

}

这里前端发送给“/app/hello”这个topic的消息就会被这个方法收到,处理完后返回的实体类也会序列化转发给"/topic/greetings",前端如果订阅了也会收到。

服务端如果要主动发送消息可以注入SimpMessagingTemplate对象调用它的以下方法:

@Autowired

private SimpMessagingTemplate simpMessagingTemplate;

simpMessagingTemplate.convertAndSend("/topic/greetings", message);

最终实现效果可以试试这个:

https://home.chengpei.top:8443/api/greetings

0

评论区