Spring boot:WebSocket+SockJS+Stomp实现广播和点对点消息传送

笔记

广播式

STS工具新建spring boot项目

使用Thymeleaf和Websocket依赖

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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.wisely</groupId>
	<artifactId>ch7_6</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>ch7_6_Websocket</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.5.6.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.7</java.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-websocket</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>

配置WebSocket

需要在配置类上使用@EnableWebSocketMessageBroker开启WebSocket支持

package com.wisely.ch7_6;

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;

@Configuration
@EnableWebSocketMessageBroker
/*
 * 通过@EnableWebSocketMessageBroker注解开启试用STOMP协议来传输基于代理(message broker)的消息,这时控制器支持使用@MessageMapping
 * 就像使用@RequestMapping一样
 * 
 */
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{

	/** 
     * 将"/endpointWisely"路径注册为STOMP端点,这个路径与发送和接收消息的目的路径有所不同
     * 这是一个端点,客户端在订阅或发布消息到目的地址前,要连接该端点, 
     */  
	
	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {//注册STOMP协议的节点(endpoint),并映射的指定的URL
		registry.addEndpoint("/endpointWisely").withSockJS();//注册一个STOMP的endpoint,并指定使用SockJS协议
		//registry.addEndpoint("/hello").setAllowedOrigins("*").withSokJS();
		//这个和客户端创建连接时的url有关,其中setAllowedOrigins()方法表示允许连接的域名,withSockJS()方法表示支持以SockJS方式连接服务器。
	}

	/** 
     * 配置了一个简单的消息代理,如果不重载,默认情况下回自动配置一个简单的内存消息代理,用来处理以"/topic"为前缀的消息。
     * 这里重载configureMessageBroker()方法, 
     * 消息代理将会处理前缀为"/topic"的消息。 
     */  
	
	@Override
	public void configureMessageBroker(MessageBrokerRegistry registry) {//配置消息代理(Message Broker)
		registry.enableSimpleBroker("/topic");//广播式应配置一个/topic消息代理
	}
	
	/* PS
	 * registry.enableSimpleBroker("/topic", "/user");这句话表示在topic和user这两个域上可以向客户端发消息。
	 * registry.setUserDestinationPrefix("/user");这句话表示给指定用户发送一对一的主题前缀是"/user"。
	 * registry.setApplicationDestinationPrefixes("/app");这句话表示客户单向服务器端发送时的主题上面需要加"/app"作为前缀。
	 */

	
}


客户端向服务端 发送消息的接受类WiselyMessage

package com.wisely.ch7_6;

/*
 * 浏览器向服务端发送的消息用此类接收
 * 
 */
public class WiselyMessage {
	private String name;
	
	public String getName(){
		return name;
	}
}


服务端向客户端发送的消息类WiselyResponse

package com.wisely.ch7_6;

/*
 * 服务端向浏览器发送的此类的消息
 * 
 */

public class WiselyResponse {
	private String responseMessage;
	
	public WiselyResponse(String responseMessage){
		this.responseMessage = responseMessage;
	}

	public String getResponseMessage() {
		return responseMessage;
	}
	
}

控制器WsController控制器WsController

package com.wisely.ch7_6;

import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

@Controller
public class WsController {
	@MessageMapping("/welcome")//当浏览器向服务端发送请求时,通过@MessageMapping映射/welcome这个地址,类似于@RequestMapping
	@SendTo("/topic/getResponse")//当服务端有消息时,监听了/topic/getResponse的客户端会接收消息
	public WiselyResponse say(WiselyMessage message) throws Exception{
		Thread.sleep(3000);
		return new WiselyResponse("Welcome," + message.getName() + "!");
	}
	
	
}

前端页面ws.html

<!--spring boot:  将stomp.min.js(STOMP协议的客户端脚本)、sockjs.min.js(SockJS的客户端脚本)以及jQuery放置在src/main/resources/static下 -->

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"> <!-- 页面使用Thymeleaf模板引擎 -->
<head>
    <meta charset="UTF-8" />
    <title>Spring Boot+WebSocket+广播式</title>

</head>
<body onload="disconnect()">
<noscript><h2 style="color: #ff0000">貌似你的浏览器不支持websocket</h2></noscript>
<div>
    <div>
        <button id="connect" onclick="connect();">连接</button>
        <button id="disconnect" disabled="disabled" onclick="disconnect();">断开连接</button>
    </div>
    <div id="conversationDiv">
        <label>输入你的名字</label><input type="text" id="name" />
        <button id="sendName" onclick="sendName();">发送</button>
        <p id="response"></p>
    </div>
</div>
<script th:src="@{sockjs.min.js}"></script>
<script th:src="@{stomp.min.js}"></script>
<script th:src="@{jquery.js}"></script>
<script type="text/javascript">
    var stompClient = null;

    function setConnected(connected) {
        document.getElementById('connect').disabled = connected; //connected为false时则可用
        document.getElementById('disconnect').disabled = !connected; //connected为false时则不可用
        document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden'; //设置conversationDiv是否可见
        $('#response').html();
    }
	
    function connect() {
        var socket = new SockJS('/endpointWisely'); //连接SockJS的endpoint名称为"/endpointWisely"
        stompClient = Stomp.over(socket);//使用STOMP自协议的WebSocket客户端
        stompClient.connect({}, function(frame) { //连接WebSocket服务端
            setConnected(true);
            console.log('Connected: ' + frame);
            stompClient.subscribe('/topic/getResponse', function(respnose){  
            	//通过stompClient.subscribe订阅/topic/getResponse目标(destination)发送的消息,这个是在控制器的@SendTo中定义的
                showResponse(JSON.parse(respnose.body).responseMessage);//页面显示接收的消息 
            });
        });
    }
	
	
    function disconnect() {
        if (stompClient != null) {
            stompClient.disconnect();
        } 
        setConnected(false);
        console.log("Disconnected");
    }

    function sendName() {
        var name = $('#name').val();
        stompClient.send("/welcome", {}, JSON.stringify({ 'name': name }));
	//通过stompClient.send 向/welcome目标(destination)发送消息,这个实在控制器的@MessageMapping中定义的
 } function showResponse(message) { var response = $("#response"); response.html(message); }</script></body></html>


WebMvcConfig中设置viewController,为ws提供路径映射

package com.wisely.ch7_6;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{

	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/ws").setViewName("/ws");
	}

	
}

点对点式:

添加Spring Security的依赖:
pom.xml添加
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-security -->
		<dependency>
		    <groupId>org.springframework.boot</groupId>
		    <artifactId>spring-boot-starter-security</artifactId>
		    <version>1.4.1.RELEASE</version>
		</dependency>

新建 WebSecurityConfig类用于配置Spring Security
package com.wisely.ch7_6;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

	/*
	 * 在内存中配置两个用户wyf和wisely,密码与用户名一致,角色为USER
	 */
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
			.withUser("wyf").password("wyf").roles("USER")
			.and()
			.withUser("wisely").password("wisely").roles("USER");
	}

	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.authorizeRequests()
			.antMatchers("/", "/login").permitAll()//设置Spring Security对"/"和"login"不拦截
			.anyRequest().authenticated()
			.and()
			.formLogin()
			.loginPage("/login")//设置Spring Security的登陆页面访问的路径为/login
			.defaultSuccessUrl("/chat")//登陆成功后转向/chat路径
			.permitAll()
			.and()
			.logout()
			.permitAll();
	}

	/*
	 * -/resources/static目录下的静态资源,Spring Security不拦截
	 */
	
	@Override
	public void configure(WebSecurity web) throws Exception {
		web.ignoring().antMatchers("/resources/static/**");
	}
	
	
}

在WebSocket配置类中的
registerStompEndpoints方法中加入:
registry.addEndpoint("/endpointChat").withSockJS();
configureMessageBroker方法中:
registry.enableSimpleBroker("/topic");
改为:
registry.enableSimpleBroker("/queue","/topic");//新增一个用于点对点的消息代理/queue

向WsController添加以下代码:
@Autowired
	private SimpMessagingTemplate messageingTemplate;//通过SimpMessagingTemplate向浏览器发送信息
	
	@MessageMapping("/chat")
	public void handleChat(Principal principal, String msg){
		//Spring MVC中可以直接在参数中获得principal,principal中包含当前用户的信息
		
		if(principal.getName().equals("wyf")){
			//测试用,实际情况需根据需求编写这里判断若发件人是wyf,则发给wisely;若是wisely则发给wyf
			
			messageingTemplate.convertAndSendToUser("wisely", "/queue/notifications", principal.getName() + "-send:" + msg);
			//通过messageingTemplate.convertAndSendToUser向用户发送信息
			//第一个参数为接收用户,第二个参数为订阅地址,第三个为消息
		}else{
			messageingTemplate.convertAndSendToUser("wyf", "/queue/notifications", principal.getName() + "-send:" + msg);
		}
	}

登陆页面login.html
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
      xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3">
<meta charset="UTF-8" />
<head>
    <title>登陆页面</title>
</head>
<body>
<div th:if="${param.error}">
    无效的账号和密码
</div>
<div th:if="${param.logout}">
    你已注销
</div>
<form th:action="@{/login}" method="post">
    <div><label> 账号 : <input type="text" name="username"/> </label></div>
    <div><label> 密码: <input type="password" name="password"/> </label></div>
    <div><input type="submit" value="登陆"/></div>
</form>
</body>
</html>

聊天页面chat.html
<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">
<meta charset="UTF-8" />
<head>
    <title>Home</title>
    <script th:src="@{sockjs.min.js}"></script>
    <script th:src="@{stomp.min.js}"></script>
    <script th:src="@{jquery.js}"></script>
</head>
<body>
<p>
    聊天室
</p>

<form id="wiselyForm">
    <textarea rows="4" cols="60" name="text"></textarea>
    <input type="submit"/>
</form>

<script th:inline="javascript">
    $('#wiselyForm').submit(function(e){
        e.preventDefault();
        var text = $('#wiselyForm').find('textarea[name="text"]').val();
        sendSpittle(text);
    });

    var sock = new SockJS("/endpointChat"); //连接endpoint
    var stomp = Stomp.over(sock);
    stomp.connect('guest', 'guest', function(frame) {
        stomp.subscribe("/user/queue/notifications", handleNotification);
	//订阅/user/queue/notifications的信息,/user必须,使用user才会发送消息到指定的用户
     //第二参数的地址和WsController中的
     //messageingTemplate.convertAndSendToUser("wisely", "/queue/notifications", principal.getName() + "-send:" + msg);
     //第二参数地址一致,相当于标志符
    });



    function handleNotification(message) {
        $('#output').append("<b>Received: " + message.body + "</b><br/>")
    }

    function sendSpittle(text) {
        stomp.send("/chat", {}, text);
    }
    $('#stop').click(function() {sock.close()});
</script>

<div id="output"></div>
</body>
</html>

WebMvcConfig添加路径映射
registry.addViewController("/login").setViewName("/login");
registry.addViewController("/chat").setViewName("/chat");

测试:略

 
 

猜你喜欢

转载自blog.csdn.net/qq_39320953/article/details/77852126
今日推荐