Spring 4引入了WebSocket API,浏览器和Web服务器可以根据WebSocket API通过WebSocket协议进行通信。通过这种方式,我们可以创建一个高度交互的UI和在线游戏,需要从服务器快速响应。SockJS在UI上提供类似WebSocket的对象。STOMP客户端用于通过WebSocket协议进行通信。我正在使用tomcat服务器来部署项目。下图为WebSocket通信的图表。
WebSocket协议
WebSocket是一种在浏览器和Web服务器之间进行通信的协议。WebSocket通过TCP协议工作。它在浏览器和Web服务器之间打开套接字连接并开始通信,它实现了浏览器与服务器全双工(full-duplex)通信——允许服务器主动发送信息给客户端。WebSocket可以轻松嵌入子协议,如STOMP。
STOMP协议
stomp是一个用于client之间进行异步消息传输的简单文本协议, 全称是Simple Text Oriented Messaging Protocol。STOMP使用连接,发送,订阅,断开等不同的命令进行通信。严格意义上来说STOMP并不是WebSocket的子协议,它是属于消息队列的一种协议,和AMQP/JMS一个层级。
SockJS
SockJS是一个java脚本库,为浏览器提供类似websocket的对象。SockJS提供跨浏览器兼容性,并支持STOMP协议与任何消息代理进行通信。SockJS的工作方式是我们需要提供URL来连接消息代理,然后让stomp客户端进行通信。
工程示例概览图
所需的jar
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.rongdu.framework</groupId> <artifactId>spring-websocket-stomp-demo</artifactId> <version>1.0</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <spring.version>4.3.18.RELEASE</spring.version> <jettyVersion>9.4.9.v20180320</jettyVersion> <jackson.version>2.7.3</jackson.version> </properties> <dependencies> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson.version}</version> </dependency> <!-- WebSocket Tomcat插件 --> <!-- <dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-websocket</artifactId> <version>7.0.53</version> </dependency> --> <!-- WebSocket Jetty插件 --> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-api</artifactId> <version>${jettyVersion}</version> </dependency> <dependency> <groupId>org.eclipse.jetty.websocket</groupId> <artifactId>websocket-server</artifactId> <version>${jettyVersion}</version> </dependency> <dependency> <groupId>javax.websocket</groupId> <artifactId>javax.websocket-api</artifactId> <version>1.1</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.java-websocket</groupId> <artifactId>Java-WebSocket</artifactId> <version>1.3.0</version> </dependency> <!-- 页面 webjar --> <dependency> <groupId>org.webjars</groupId> <artifactId>webjars-locator-core</artifactId> <version>0.35</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>sockjs-client</artifactId> <version>1.0.2</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>stomp-websocket</artifactId> <version>2.3.3</version> </dependency> <dependency> <groupId>org.webjars</groupId> <artifactId>jquery</artifactId> <version>3.1.0</version> </dependency> <!-- RabbitMQ --> <dependency> <groupId>com.rabbitmq</groupId> <artifactId>amqp-client</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>org.springframework.amqp</groupId> <artifactId>spring-rabbit</artifactId> <version>1.4.5.RELEASE</version> </dependency> <!--slf4j--> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-slf4j-impl</artifactId> <version>2.7</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>utf-8</encoding> </configuration> </plugin> </plugins> </build> </project>
web.xml配置
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <context-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/config/spring/*-beans.xml</param-value> </context-param> <!-- 编码过滤器 --> <filter> <filter-name>characterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>ASYNC</dispatcher> </filter-mapping> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <servlet> <servlet-name>springServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/config/web/web-main.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>springServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> </web-app>
spring的mvc-servlet.xml配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:task="http://www.springframework.org/schema/task" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:websocket="http://www.springframework.org/schema/websocket" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-4.3.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.3.xsd http://www.springframework.org/schema/websocket http://www.springframework.org/schema/websocket/spring-websocket.xsd"> <!-- 自动扫包 --> <context:component-scan base-package="com.huatech.controller"/> <!-- 提供请求分发、数据绑定、数据转换等操作 --> <mvc:annotation-driven/> <!-- application-destination-prefix 逗号分隔的前缀列表,用于匹配已处理消息的目标 user-destination-prefix 用于标识用户目标的前缀。 不解析任何不以给定前缀开头的目的地。 默认值为:/user/ --> <websocket:message-broker application-destination-prefix="/app" user-destination-prefix="/queue"> <websocket:stomp-endpoint path="/websocket-address" > <websocket:sockjs/> </websocket:stomp-endpoint> <websocket:simple-broker prefix="/topic,/queue"/> </websocket:message-broker> <!-- 视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/js/"/> <property name="suffix" value=".jsp"/> <property name="contentType" value="text/html; charset=UTF-8"/> </bean> <!-- webjars 静态资源 --> <mvc:resources mapping="/webjars/**" location="classpath:/META-INF/resources/webjars/" > <mvc:resource-chain resource-cache="false" auto-registration="false"> <mvc:resolvers> <bean class="org.springframework.web.servlet.resource.WebJarsResourceResolver"/> <bean class="org.springframework.web.servlet.resource.PathResourceResolver"/> </mvc:resolvers> </mvc:resource-chain> </mvc:resources> <!-- 本地资源 --> <mvc:resources mapping="/js/**" location="/js/"/> <!-- 默认首页 --> <mvc:view-controller path="/" view-name="index"/> <!-- scheduler配置 --> <task:executor id="executor" pool-size="5" /> <task:scheduler id="scheduler" pool-size="5" /> <task:annotation-driven executor="executor" scheduler="scheduler" /> </beans>
添加控制器类
package com.huatech.controller; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.messaging.handler.annotation.MessageMapping; import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.messaging.simp.annotation.SendToUser; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Controller; @Controller public class TopicController { private static final Logger LOGGER = LoggerFactory.getLogger(TopicController.class); @Autowired private SimpMessagingTemplate template; /*收到消息后广播推送*/ @MessageMapping("/boardcast") @SendTo("/topic/boardcast") public String sendtousers(String s){ LOGGER.info(s); return "我是广播消息"; } /*收到消息后精准投送到单个用户*/ @MessageMapping("/precise") /*broadcast = false避免把消息推送到同一个帐号不同的session中*/ @SendToUser(value = "/topic/precise",broadcast = false) public String sendtouser(String s){ LOGGER.info(s); return "我是精准投送消息"; } /*下面2个是不用接收消息动态发送消息的方法*/ /*Scheduled为定时任务,演示*/ @Scheduled(fixedDelay = 1500) public void boardcast(){ this.template.convertAndSend("/topic", "来自服务器广播消息"); } @Scheduled(fixedDelay = 1500) public void precise(){ this.template.convertAndSendToUser("abc", "/message","来自服务器精准投送消息"); } }
index.jsp示例页面
<%@ page language="java" pageEncoding="UTF-8"%> <html> <head> <meta charset="utf-8"/> <title>spring-websocket-stomp</title> </head> <body> <h1>spring-websocket-stomp</h1> <button id="btn-send">广播消息</button> <button id="btn-send2">精准投送消息</button> <button id="btn-send3">动态广播</button> <button id="btn-send4">动态精准投送</button> </body> </html> <script src="webjars/jquery/jquery.min.js"></script> <script src="webjars/sockjs-client/sockjs.min.js"></script> <script src="webjars/stomp-websocket/stomp.min.js"></script> <script src="js/app.js"></script>
app.js
var stompClient = null; var subscription = null; $(function () { var ws = new SockJS("/websocket-address"); stompClient = Stomp.over(ws); stompClient.connect({'client-id': 'my-client'},function () { }); $("#btn-send").click(function () { if(subscription != null){subscription.unsubscribe();} subscription = stompClient.subscribe("/topic/boardcast", function(){}); stompClient.send("/app/boardcast",{},"请求广播"); }); $("#btn-send2").click(function () { if(subscription != null){subscription.unsubscribe();} subscription = stompClient.subscribe("/queue/topic/precise", function(){}); stompClient.send("/app/precise",{},"请求精准投送"); }); // 请求动态广播 $("#btn-send3").click(function () { if(subscription != null){subscription.unsubscribe();} subscription = stompClient.subscribe("/topic", function(){}); }); // 请求精准投送 $("#btn-send4").click(function () { if(subscription != null){subscription.unsubscribe();} subscription = stompClient.subscribe("/queue/abc/message", function(){}); }); });
遇到的问题
1、spring引入webjars页面报404问题
参考:https://blog.csdn.net/chao_1990/article/details/53449494
2、Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.ObjectMapper.canSerialize(Ljava/lang/Class;Ljava/util/concurrent/atomic/AtomicReference;)Z
答:jackson版本冲突
3、@MessageMapping注解配置后不起作用
答:websocket相关的配置需通过mvc进行初始化,而不是交给IOC
参考:
相关推荐
Java开发基于SpringBoot+WebSocket+Redis分布式即时通讯群聊系统。一个基于Spring Boot + WebSocket + Redis,可快速开发的分布式即时通讯群聊系统。适用于直播间聊天、游戏内聊天、客服聊天等临时性群聊场景。 ...
本项目结合这三个技术,构建了一个整合的示例,旨在展示如何在SpringBoot应用中实现安全的、基于WebSocket的实时通信。 首先,SpringBoot是由Pivotal团队提供的一个简化Spring应用初始搭建以及开发过程的框架。它...
基于Spring Boot + WebSocket + Redis,可快速开发的分布式即时通讯群聊系统.zip 1、该资源内项目代码经过严格调试,下载即用确保可以运行! 2、该资源适合计算机相关专业(如计科、人工智能、大数据、数学、电子信息...
总结,结合Spring Boot和WebSocket,我们可以轻松地构建出实时交互的Web应用。在后端,我们配置WebSocket支持,实现消息处理;在前端,使用jQuery和SockJS库来管理WebSocket连接和消息交换。这样的组合为现代Web应用...
在现代Web开发中,实时通信已经成为一个不可或缺的功能,Spring、Netty和WebSocket的结合为构建高性能、低延迟的实时应用提供了强大的解决方案。本实例将详细探讨如何利用这三种技术进行集成,实现高效的双向通信。 ...
4. **Spring Security**: Spring Security是Spring框架的一个子项目,提供了一整套安全访问控制解决方案。它包括认证、授权等功能,可以保护Web应用免受攻击,如XSS、CSRF等。在SpringBoot中,Spring Security可以...
本知识点将介绍如何在Spring Boot与Vue前后端分离架构中集成WebSocket,并实现带有身份认证的消息推送功能。这是实现即时通讯、实时数据更新等应用场景的常用技术方案。 ### 1. WebSocket简介 WebSocket是一种在...
这本学习笔记应该详细介绍了如何在 Spring 应用中集成 Spring MVC 和 WebSocket,包括设置项目结构、配置 Spring 容器、编写 WebSocket 处理端点类、实现客户端连接及消息交互的示例代码,并可能涉及到如何在 Spring...
SpringBoot +Vue3 +Jwt+Editor+WebSocket+Echarts小型超市进销管理系统 SpringBoot +Vue3 +Jwt+Editor+WebSocket+Echarts小型超市进销管理系统 SpringBoot +Vue3 +Jwt+Editor+WebSocket+Echarts小型超市进销管理...
springBoot+webSocket+uniapp实现实时聊天功能
本项目为spring-boot+webSocket实现的向日葵远程控制项目 向日葵是一款很好用的远程操作软件。 一直很好奇这种软件的基本原理是如何的? 今天带大家通过一个简单的项目来探究一下,并实现一个简单的远程操控软件 ...
基于Vue+SpringBoot+WebSocket实现Web端聊天系统+网络通信+心跳+客户端服务端+后端java+mysql 项目经过严格测试,确保可以运行! 下载代码后,先配置好yml文件:数据库和微信号appid、secret(other包下面有数据库)...
如何使用Postman来发送数据到后端Spring Boot应用程序,并使用WebSocket建立实时通信通道,以便后端应用程序可以向Postman推送消息。 学会这个方法,就能缩短你的项目开发周期,以及提高你的开发效率。如果提出您的...
- SpringBoot简化了Spring应用的初始搭建以及开发过程,通过自动配置和起步依赖特性,使得创建独立的、生产级别的基于Spring的应用变得容易。 - 应用程序通常可以通过`main`方法启动,并且可以嵌入Tomcat或Jetty等...
2. **WebSocket Stomp协议**:在Spring Boot中,通常会使用STOMP(Simple Text Oriented Messaging Protocol)作为WebSocket的应用层协议。STOMP简化了消息的格式,使得客户端和服务器之间可以交换消息。Spring ...
在本项目"spring+websocket+hibernate小例子"中,开发者通过集成Spring框架、WebSocket技术和Hibernate ORM,创建了一个小型的交互式应用。这个应用能够实现实时通信,并且能够与数据库进行数据交互,展示了现代Web...
4. **Netty的WebSocket服务器**:虽然Spring提供了WebSocket支持,但在某些场景下,我们可能需要更底层的控制,比如高性能或高度定制的需求,这时可以使用Netty。Netty是一个异步事件驱动的网络应用框架,特别适合...
在本示例中,我们将深入探讨如何在Spring4框架中集成WebSocket技术,为Web应用程序提供实时通信功能。WebSocket协议是一种在客户端和服务器之间建立长期连接的协议,它允许双向通信,使得数据能够在任意时刻从服务器...
基于Springboot+Websocket的简单聊天室 基于Springboot+Websocket的简单聊天室 基于Springboot+Websocket的简单聊天室 基于Springboot+Websocket的简单聊天室 基于Springboot+Websocket的简单聊天室 基于Springboot+...