24-SpringBoot——核心-WebSocket
来源:互联网 发布:最近比较火的网络歌曲 编辑:程序博客网 时间:2024/06/07 14:29
SpringBoot——核心-WebSocket
【博文目录>>>】
【项目源码>>>】
【WebSocket】
WebSocket 为浏览器和服务端提供了双工异步通信的功能,即浏览器可以向服务端发送消息,服务端也可以向浏览器发送消息。WebSocket 需浏览器的支持,如IE 10+、Chrome 13+,Firefox 6+,这对我们现在的浏览器来说都不是问题。
WebSocket 是通过一个socket 来实现双工异步通信能力的。但是直接使用WebSocket (或者SockJS: WebSocket 协议的模拟,增加了当浏览器不支持WebSocket 的时候的兼容支持)协议开发程序显得特别烦琐,我们会使用它的子协议STOMP ,它是一个更高级别的协议, STOMP协议使用一个基于帧(企ame )的格式来定义消息,与HTTP 的request 和response 类似(具有类似于@RequestMappir屯的@MessageMapping ),我们会在后面实战内容中观察STOMP 的帧。
Spring Boot 对内嵌的Tomcat (7 或者8 )、Jetty9 和Undertow 使用WebSocket 提供了支持。配置源码存于org.springframework.boot.autoconfigure.websocket。Spring Boot 为WebSockct 捉供的stater pom 是spring-boot-starter-websocket
广播式
广播式即服务端有消息时,会将消息发送给所有连接了当前endpoint 的浏览器。
( 1 )配置WebSocket ,需要在配置类上使用@EnableWebSocketMessageBroker 开启Web Socket 支持,并通过继承AbstractWebSocketMessageBrokerConfigurer 类,重写其方法来配置WebSocket。
当一个浏览器发送一个消息到服务端时,其他浏览器也能接收到从服务端发送来的这个消息。开启三个浏览器窗口,并访问http://localhost: 8080/ws ,分别连接服务器。然后在一个浏览器中发送一条消息,其他浏览器接收消息。连接服务端,可以看到进行效果。所有浏览器接收服务端发送的消息。
【代码实现】
ch0707-SpringBoot-Web-Websocket
package com.example.spring.boot.websocket;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * Author: 王俊超 * Date: 2017-07-17 07:44 * All Rights Reserved !!! */@SpringBootApplicationpublic class SampleApplication { public static void main(String[] args) { SpringApplication.run(SampleApplication.class, args); }}
package com.example.spring.boot.websocket;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;/** * Author: 王俊超 * Date: 2017-07-17 07:39 * All Rights Reserved !!! */@Configurationpublic class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/ws").setViewName("/ws"); }}
package com.example.spring.boot.websocket;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;/** * 通过@EnableWebSocketMessageBroker 注解开启使用STOMP 协议来传输基于代理message broker )的消息, * 这时控制器支持使用@MessageMapping ,就像使用@RequestMapping一样。 * Author: 王俊超 * Date: 2017-07-17 07:32 * All Rights Reserved !!! */@Configuration@EnableWebSocketMessageBrokerpublic class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { /** * 注册STOMP 协议的节点( endpoint ),并映射的指定的URL * @param registry */ @Override public void registerStompEndpoints(StompEndpointRegistry registry) { // 泣册一个STOMP 的endpoint ,并指定使用SockJS 协议 registry.addEndpoint("/endpointWisely").withSockJS(); } /** * 配置 消息代理( Message Broker )。 * @param registry */ @Override public void configureMessageBroker(MessageBrokerRegistry registry) { // t·:r苗式应配捏一个/topic 消息代现。 registry.enableSimpleBroker("/topic"); }}
package com.example.spring.boot.websocket;/** * Author: 王俊超 * Date: 2017-07-17 07:35 * All Rights Reserved !!! */public class WiselyMessage { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; }}
package com.example.spring.boot.websocket;/** * Author: 王俊超 * Date: 2017-07-17 07:38 * All Rights Reserved !!! */public class WiselyResponse { private String responseMessage; public WiselyResponse(String responseMessage) { this.responseMessage = responseMessage; } public String getResponseMessage() { return responseMessage; }}
package com.example.spring.boot.websocket;import org.springframework.messaging.handler.annotation.MessageMapping;import org.springframework.messaging.handler.annotation.SendTo;import org.springframework.stereotype.Controller;/** * Author: 王俊超 * Date: 2017-07-17 07:36 * All Rights Reserved !!! */@Controllerpublic class WsController { // ①当浏览器向服务端发送请求时,通过@MessageMapping 映射/welcome 这个地址, // 类似于@RequestMapping @MessageMapping("/welcome") // 当服务端有消息时,会对订阅了@SendTo 中的路径的浏览器发送消息。 @SendTo("/topic/getResponse") public WiselyResponse say(WiselyMessage message) throws InterruptedException { Thread.sleep(3000); return new WiselyResponse("Welcome, " + message.getName()); }}
<!DOCTYPE html><html xmlns:th="http://www.thymeleaf.org"><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; document.getElementById('disconnect').disabled = !connected; document.getElementById('conversationDiv').style.visibility = connected ? 'visible' : 'hidden'; $('#response').html(); } function connect() { // 连接SockJS 的endpoint 名称为“/endpointWisely” var socket = new SockJS('/endpointWisely'); // 使用STOMP 子协议的WebSocket 客户端 stompClient = Stomp.over(socket); // 连接WebSocket 服务端。 stompClient.connect({}, function (frame) { 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 目标( destination )发送消息, // 这个是在控制器的@MessageMapping 中定义的。 stompClient.send("/welcome", {}, JSON.stringify({'name': name})); } function showResponse(message) { var response = $("#response"); response.html(message); }</script></body></html>
点对点式
广播式有自己的应用场景,但是广播式不能解决我们一个常见的场景,即消息由谁发送、由谁接收的问题。
【代码实现】
ch0708-SpringBoot-Web-Websocket-点对点通讯
package com.example.spring.boot.websocket.p2p;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;/** * Author: 王俊超 * Date: 2017-07-17 07:44 * All Rights Reserved !!! */@SpringBootApplicationpublic class SampleApplication { public static void main(String[] args) { SpringApplication.run(SampleApplication.class, args); }}
package com.example.spring.boot.websocket.p2p;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;/** * Author: 王俊超 * Date: 2017-07-17 08:01 * All Rights Reserved !!! */@Configurationpublic class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("/login"); registry.addViewController("/chat").setViewName("/chat"); }}
package com.example.spring.boot.websocket.p2p;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;/** * Author: 王俊超 * Date: 2017-07-17 07:51 * All Rights Reserved !!! */@Configuration@EnableWebSecuritypublic class WebSecurityConfig extends WebSecurityConfigurerAdapter { @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(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 在内存中分别配置两个用户wjc和wisely,密码和用户名一致,角色是USER auth.inMemoryAuthentication() .withUser("wjc").password("wjc").roles("USER") .and() .withUser("wisely").password("wisely").roles("USER"); } @Override public void configure(WebSecurity web) throws Exception { // /resources/static/目录下的静态资源, Spring Security 不拦截。 web.ignoring().antMatchers("/resources/static/*"); }}
package com.example.spring.boot.websocket.p2p;import org.springframework.context.annotation.Configuration;import org.springframework.messaging.simp.config.MessageBrokerRegistry;import org.springframework.web.method.annotation.AbstractWebArgumentResolverAdapter;import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;import org.springframework.web.socket.config.annotation.StompEndpointRegistry;/** * Author: 王俊超 * Date: 2017-07-17 07:56 * All Rights Reserved !!! */@Configuration@EnableWebSocketMessageBrokerpublic class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{ @Override public void registerStompEndpoints(StompEndpointRegistry registry) { // 注册一个名为/endpointChat的endpoint registry.addEndpoint("/endpointChat").withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { // 点对点式应增加一个/queue 消息代理。 registry.enableSimpleBroker("/queue", "/topic"); }}
package com.example.spring.boot.websocket.p2p;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.messaging.handler.annotation.MessageMapping;import org.springframework.messaging.simp.SimpMessagingTemplate;import org.springframework.stereotype.Controller;import java.security.Principal;/** * Author: 王俊超 * Date: 2017-07-17 07:57 * All Rights Reserved !!! */@Controllerpublic class WsController { @Autowired private SimpMessagingTemplate template; @MessageMapping("/chat") public void handleChat(Principal principal, String message) { if (principal.getName().equals("wjc")) { template.convertAndSendToUser("wisely", "/queue/notifications", principal.getName() + "-send: " + message); }else { template.convertAndSendToUser("wjc", "/queue/notifications", principal.getName() + "-send: " + message); } }}
<!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"); //1 var stomp = Stomp.over(sock); stomp.connect('guest', 'guest', function (frame) { stomp.subscribe("/user/queue/notifications", handleNotification);//2 }); function handleNotification(message) { $('#output').append("<b>Received: " + message.body + "</b><br/>") } function sendSpittle(text) { stomp.send("/chat", {}, text);//3 } $('#stop').click(function () { sock.close() });</script><div id="output"></div></body></html>
<!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"><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>
- 24-SpringBoot——核心-WebSocket
- 18-SpringBoot——核心-基本配置
- 19-SpringBoot——核心-Profile配置
- 20-SpringBoot——核心-运行原理
- 21-SpringBoot——核心-Web配置
- 22-SpringBoot——核心-Tomcat配置
- 23-SpringBoot——核心-Favicon配置
- SpringBoot学习——websocket组件学习在线聊天室demo
- springboot websocket
- SpringBoot-Websocket
- SpringBoot + WebSocket
- 25-SpringBoot——核心-Spring Data JPA
- 26-SpringBoot——核心-Spring Data REST
- 27-SpringBoot——核心-声明式事务
- 28-SpringBoot——核心-数据缓存Cache
- 29-SpringBoot——核心-非关系型数据库NoSQL
- 30-SpringBoot——核心-企业级开发-Spring Security
- 31-SpringBoot——核心-企业级开发-SpringBatch
- CoordinatorLayout.Behavior方法详解
- 微信支付异步处理支付结果-公众号支付开发-视频教程6
- 微信扫码支付开发-公众号支付开发-视频教程7
- C++ 堆排序和堆的其他基本操作
- ST(RMQ)算法(在线)求LCA
- 24-SpringBoot——核心-WebSocket
- 密钥,证书,签名相关概念
- Yii2.0小部件GridView(数据展示)①
- 5分钟在大米云上基于WordPress搭建一个博客
- Redis主从复制,哨兵,集群创建与增删操作
- 统计学习三要素——模型、策略、算法
- AIM Tech Round 4 (Div. 2) A. Diversity
- Python Study(02)之 Context Manager
- JAVA-初步认识01-第一章