`
hbxflihua
  • 浏览: 660773 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Spring4+WebSocket+Socket+STOMP+Jetty构建示例

阅读更多

 

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

参考:

https://stackoverflow.com/questions/42441837/messagemapping-does-not-work-with-spring-security-and-mvc

  • 大小: 8.8 KB
  • 大小: 14.2 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics